Помогите Запрос к Базе Данных

Статус
Закрыто для дальнейших ответов.

tiu

Активный участник
Пользователь
Баллы
61
Имя в Minecraft
tiu
Добрый день, я тут разрабатывал некий датацентер и случился казус.

У нас есть некий метод get(String data), который отправляет запрос на БД. Ответ же на запрос приходит в другом методе answer(String data). Внимание, ВОПРОС: Как нужно правильно сделать запрос к бд, чтобы при этом задержать метод get(), но при этом не останавливая основной поток, т.к. если остановить осн поток, ответ не дойдёт до получателя. Есть некие наработки. Поток t создан исключительно в целях тестирования.

Просьба скидывать ответ с примерами, а то я тупой:creeper:

PHP:
package DataCenter;

import org.bukkit.Bukkit;

import DataCenter.enums.Packet;

public class test{
  
    private static String answer="";
  
    private static Object lock=new Object();
  
    public static void answer(String data) {
        Bukkit.getLogger().info("answer="+data);
        synchronized (lock) {
            answer=data;
            lock.notifyAll();
        }
    }
  
    private static Packet last;
  
    private static Runnable r=new Runnable() {
        @Override
        public void run() {
            Messenger.SendRequest(last, Messenger.server);
            Bukkit.getLogger().info("SubThread started");
            synchronized (lock) {
                try {
                    lock.wait();
                    Bukkit.getLogger().info("NOTIFY");
                    notifyAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return;
            }
        }
    };
  
    public static String get(Packet packet) {
        Thread t=new Thread(r);
        last=packet;
        Messenger.SendRequest(last, Messenger.server);
        t.start();
      
        synchronized (lock) {
            try {
                lock.wait(2000);
                return answer;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return answer;
        }
    }
  
}

P.s. Если чо, объект Packet потом преращается в нормальное сообщение
P.s.s. Вся проблема в строчке lock.wait(2000);, которая стопает осн поток и мешает ответу добраться до сервера. Запихать приём в отд поток-не вариант(
 
Убирай свои локи и вейты, убирай раннейблы и потоки. Полный мусор. Возможно, когда-то ты разберёшься с потоками нормально, и напишешь что-то стоящее, но сейчас это лишь усложняет тебе жизнь.

Единственное, что нужно сделать - отправить запрос в бд и получить ответ НЕ в основном потоке. Для этого отлично подходит баккитовский шедулер:
PHP:
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
    // Запрос к БД
    // Обработка результата запроса и действия после обработки
});

Можно сделать отдельный метод для отправки асинхронных запросов, и всегда использовать именно его. Выглядеть это может примерно так:
PHP:
public void queryAsync(PreparedStatement statement, Consumer<ResultSet> onResult) {
    Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
        try {
            ResultSet resultSet = statement.executeQuery();
            onResult.accept(resultSet);
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

Это самое простое, что вообще можно придумать. Само собой, когда разберёшься, сможешь переделать под себя - например, индивидуально под каждый запрос организовать обработку ошибок.

Запихать приём в отд поток-не вариант(
Если ты хочешь принимать ответ в основном серверном потоке - значит это будет уже не асинхронный запрос в БД, а синхронный. И основной поток будет дожидаться ответа, а все остальные механики сервера в этот момент будут простаивать. Никак иначе это работать не может.

В основном потоке можно сделать уже обработку ответа, полученного асинхронно. Например, можно запустить синхронный шедулер после асинхронного получения ответа. Но какой в этом смысл?
 
Последнее редактирование:
Боюсь, я изначально неправильно выразился. Под БД подразумевается другой сервер, а канал общения - PluginMessager, что ограничивает меня моментально. Безусловно, это костыль похлеще отправки сообщения игроку пакетами без JSON'а, но так было интересней

Единственное, что нужно сделать - отправить запрос в бд и получить ответ НЕ в основном потоке. Для этого отлично подходит баккитовский шедулер

Что-ж, получается, нужно как-то в шедулер или в отдельный поток впихать implements PluginMessageListener, чтобы принимать сообщения в отдельном потоке, а потом размораживать основной поток и ретурнить ответ из метода get().

Или создать свой протокол общения между серверами без банжи, но я туда лезть пока не хочю

Или просто на SQL перейти:mad:

Вообщем, если вы знаете как впихать implements PluginMessageListener в асинк поток, я буду рад узнать этот способ, или другие идеи решения проблем) В любом случае, спасибо за помощь
 
С PluginMessages ситуация совсем иная, нежели с БД. PluginMessage's является системой, в которой в любой момент любая сторона можно отправить любое сообщение. При отправке сообщений не происходит блокировка потока, т.к. ответа ожидать бесполезно. Короче говоря, полноценной готовой системы вопрос-ответ в PluginMessage's просто не существует.

Но можно попробовать организовать подобие такой системы самостоятельно. Тут уже всё зависит от реализации отвечающей стороны. Кто отвечает на запрос? Банжа? Плагин на банже? Клиентский мод?
 
С PluginMessages ситуация совсем иная, нежели с БД. PluginMessage's является системой, в которой в любой момент любая сторона можно отправить любое сообщение. При отправке сообщений не происходит блокировка потока, т.к. ответа ожидать бесполезно. Короче говоря, полноценной готовой системы вопрос-ответ в PluginMessage's просто не существует.

Но можно попробовать организовать подобие такой системы самостоятельно. Тут уже всё зависит от реализации отвечающей стороны. Кто отвечает на запрос? Банжа? Плагин на банже? Клиентский мод?

На запрос отвечает другой сервер с Bukkit'ом, а сам запрос отправляется через банжу. После обработки сервер-датацентр отправляет ответ, но на обработку может потребоваться время, и на сервере-датацентре реализована очередь запросов. То есть, схема следующая: Сервер n=>BungeeCord=>DataCenter=>BungeeCord=>Server n
 
Не нужно ничего изобретать и городить потоки. В баките есть sendPluginMessage и слушатель ответов PluginMessageListener. Собственно и в банжекорде тоже это имеется. Все уже изобрели за тебя.
 
Не нужно ничего изобретать и городить потоки. В баките есть sendPluginMessage и слушатель ответов PluginMessageListener. Собственно и в банжекорде тоже это имеется.
Ситуация немного иная. У меня есть метод get(), который должен возвращать String, получаемый после запроса при ответе в методе OnPluginMessageReceived(). То есть нужно отправить запрос и дождаться ответа, замораживая при этом поток, из которого пришёл запрос, не замораживая основной поток, или пихнуть OnPluginMessageReceived() в отдельный поток, и заморозить основной поток. Этом-то и проблема, что вроде как нету способа пихнуть OnPluginMessageReceived() в отдельный поток... А так всё довольно просто.
 
Ситуация немного иная. У меня есть метод get(), который должен возвращать String, получаемый после запроса при ответе в методе OnPluginMessageReceived(). То есть нужно отправить запрос и дождаться ответа, замораживая при этом поток, из которого пришёл запрос, не замораживая основной поток, или пихнуть OnPluginMessageReceived() в отдельный поток, и заморозить основной поток. Этом-то и проблема, что вроде как нету способа пихнуть OnPluginMessageReceived() в отдельный поток... А так всё довольно просто.
Нифига не понял, но очень интересно. Ты просто шлешь sendPluginMessage и слушаешь ответ в PluginMessageListener. Никаких блокировок делать не нужно. Как ответ придет тогда и ивент отработает.
 
Нифига не понял, но очень интересно. Ты просто шлешь sendPluginMessage и слушаешь ответ в PluginMessageListener. Никаких блокировок делать не нужно. Как ответ придет тогда и ивент отработает.
Боюсь, что конкретно на примере реализации БД не всё так просто. У нас есть метод get(), который ретурнит String с ответом со стороны БД. То есть, в методе get() мы должны дождаться ответа, а потом ретурнуть ответ из onPluginMessageRecieved().
 
Ситуация немного иная. У меня есть метод get(), который должен возвращать String, получаемый после запроса при ответе в методе OnPluginMessageReceived(). То есть нужно отправить запрос и дождаться ответа, замораживая при этом поток, из которого пришёл запрос, не замораживая основной поток, или пихнуть OnPluginMessageReceived() в отдельный поток, и заморозить основной поток. Этом-то и проблема, что вроде как нету способа пихнуть OnPluginMessageReceived() в отдельный поток... А так всё довольно просто.
То, что ты хочешь сделать, реализовать теоретически возможно. Действительно придётся потанцевать с потоками и их блокировками. Фактически нужно будет реализовывать блокирующую операцию получения ответа на какой-то запрос. Боюсь, для тебя это будет пока сложновато.

Но есть альтернатива. Предлагаю тебе воспользоваться следующим способом.

При создании запроса можно запоминать куда-то действия, которые будут выполнены при получении ответа. Эти действия можно заключить, например, в объект Consumer<ByteArrayDataInput>. Но я предлагаю для удобства объявить отдельный класс/интерфейс MessagingRequest. У него будут два метода: .send() и .onResponse(). Первый будет вызываться для отправки данных. А второй будет вызываться при получении ответа на этот запрос.

Но тут встаёт вопрос - как по ответу понять, какой был задан вопрос, какие действия нужно выполнить?

Всё будет работать идеально, только если новый запрос отправляется в "DataCenter" только после получения ответа на старый вопрос, и никак иначе.

Если же нужно отправить сразу несколько запросов подряд, не дожидаясь ответа - придётся где-то хранить целую очередь из действий, которые выполнятся при ответе. Для подобного можно использовать, например List<MessagingRequest>. При получении ответа просто берём первый элемент (и сразу удаляем его из списка) - это и будет объект запорса, который отправлялся раньше всех (в текущей очереди).

Но если сервер отвечает на вопросы не в том порядке, в котором они приходят на сервер, то всё становится ещё сложнее. В этом случае придётся определять необходимый MessagingRequest по самому содержимому ответа. Сделать это можно, например, путём добавления к каждому запросу какой-то уникальной подписи. Это может быть что угодно: например, рандомный int. При отправке ответа "DataCenter" обязан указать эту же подпись в ответе.
Таким образом, при получении ответа у нас будет уникальный номер запроса. А это значит, что все запросы можно хранить в каком-нибудь Map<Integer, MessagingRequest>.

Ну и, собственно, когда мы поняли, какой у нас MessagingRequest, мы просто вызываем у него метод onResponse(). Естественно, в этом методе нужно будет передать массив полученных данных и объект игрока, которому пришло сообщение (если это требуется).

Абсолютно всё разжёвывать не стал, но надеюсь, что ты разберёшься и сам реализуешь моменты, о которых я не рассказал
 
Боюсь, что конкретно на примере реализации БД не всё так просто. У нас есть метод get(), который ретурнит String с ответом со стороны БД. То есть, в методе get() мы должны дождаться ответа, а потом ретурнуть ответ из onPluginMessageRecieved().
Ну как ответ от бд придет, так и отправится ответный sendPluginMessage В чем твоя проблема то?
 
[Тема закрыта] Итак, объясню подробней решение, использованное мной для моих потомков. Как и сказал многоуважаемый Dymeth, всё достаточно просто. Создаём объект отправляемого пакета данных и присваиваем ему некоторое значение UUID(Уникальный Идентификатор). Перед отправлением пакета мы должны запомнить его в некоторый Map<Integer, Thread>, где Integer - наш уникальный идентификатор, а Thread - поток, запускаемый при получении ответа. в Thread должен находится метод (ниже пример его создания)
Код:
Thread test=new Thread() {
     @Override
      public void run() {
            String answer=Query.answer;
            test(answer);
      }
               
      public void test(String answer) {
             Bukkit.getLogger().info(answer);
      }
};

Также в классе с Map<Integer, Thread> должен находиться метод, запускаемый при получении PluginMessage. В этом методе мы присваиваем указанной в классе переменной answer значение, полученное в ответе со стороны БД, а затем запускаем поток, указанный в мепе выше. Поток выполнит действия, указанные вами, не останавливая основной поток сервера, и, соответственно, не замедляя его работу. Объяснил максимально подробно и понятно, но опустил некоторые моменты. Желаю удачи.
 
Статус
Закрыто для дальнейших ответов.
Назад
Верх