Как ждать подписки?

У меня есть следующий код js:

stompClient.subscribe('/topic/clients', function (calResult) {
    updateClientsTable(JSON.parse(calResult.body));
});
$.get("/clients", null);

и следующий код сервера (последняя строка вызывает его):

 @GetMapping(value = {"/clients"})
 @ResponseBody
 public void loadClients() {
      brokerMessagingTemplate.convertAndSend("/topic/clients", clientService.getClientList());
 }

Когда-то front-end пропускает результат $.get("/clients", null);

Как я понимаю, проблема: в момент получения результата на лицевой стороне подписки не происходит.

если поставить $.get("/clients", null); ниже в коде - все работает нормально.

Можете ли вы объяснить, как ждать подписки?

Ответ 1

Как уже упоминалось @light_303, смешивание HTTP-запросов с механизмом уведомления не очень хорошо. Вы можете зарегистрировать момент, когда клиент подключается (запрос GET на /clients), но вы не можете зарегистрироваться, когда он отключается.

Вы должны думать одним из следующих способов. Когда пользователь подписывается на /topic/clients:

  • Вы индивидуально отправляете ему ответ со всем списком клиентов, а затем только нажимаете обновления.
  • Вы индивидуально отправляете ему текущее время сервера или какой-то идентификатор, а затем только обновляете обновления. Пользователь использует заданное время /ID в запросе GET для /clients и получает полный список клиентов в этот момент. Этот параметр может быть хорошим в ситуации, когда у вас есть инкрементные обновления (то есть добавление новых элементов в список), и в противном случае это не так хорошо.

Отметьте этот вопрос: Отправка сообщения конкретному пользователю в Spring Websocket.

Это на самом деле смешно, как Spring может усложнить ситуацию. Я рекомендую вам посмотреть другие фреймворки для веб-общения в реальном времени, такие как Vert.x или Netty и язык программирования Go Go. Используйте WebSockets или SockJS вместо STOMP. Все эти технологии могут дать вам более гибкое и эффективное решение. Кроме того, проверьте Centrifugo проект, возможно, это относится к вашей задаче.

Ответ 2

Я думаю, что было бы разумнее не смешивать запросы REST с этим шаблоном обмена сообщениями.

Рассматривали ли вы отправку команды "updateClients" через SockJS в канал "/apps/updateClients", который отвечает на канал "/topic/clients"?

Ответ 3

Вы можете использовать аннотацию @SubscribeMapping от spring-messaging.

Если у вас spring-messaging настроено, как описано здесь и здесь, серверный код может выглядеть следующим образом:

@Controller
public class MessagingController {
    @SubscribeMapping("/clients")
    public List<Client> loadClients() {
        return clientService.getClientList();
    }
}

Таким образом, вам не нужно вызывать $.get("/clients", null);, потому что обработчик сообщения JS получает результат loadClients() call сразу после того, как происходит подписка. Код JS будет выглядеть так:

stompClient.subscribe('/topic/clients', function (calResult) {
    updateClientsTable(JSON.parse(calResult.body));
});