2015-03-22 5 views
6

На стороне клиента JavaScript У меня естьStomp над WebSocket использованием Spring и sockJS сообщение потеряли

stomp.subscribe("/topic/path", function (message) { 
     console.info("message received"); 
    }); 

А на стороне сервера

public class Controller { 
    private final MessageSendingOperations<String> messagingTemplate; 
    @Autowired 
    public Controller(MessageSendingOperations<String> messagingTemplate) { 
     this.messagingTemplate = messagingTemplate; 
    } 
    @SubscribeMapping("/topic/path") 
    public void subscribe() { 
    LOGGER.info("before send"); 
    messagingTemplate.convertAndSend(/topic/path, "msg"); 
    } 
} 

Из этой установки, я иногда (примерно раз в 30 обновление страницы), в результате чего происходит сброс сообщений, что означает, что я не вижу ни сообщения «сообщение получено» на стороне клиента, ни трафик веб-рассылки с помощью инструмента отладки Chrome.

«перед отправкой» всегда регистрируется на стороне сервера.

Похоже, что MessageSendingOperations не готов, когда я вызываю его в методе subscribe(). (если я поставил Thread.sleep (50), перед тем как позвонить messagingTemplate.convertAndSend, проблема исчезнет (или, что гораздо реже будет воспроизведена))

Интересно, если кто-то испытал то же самое до и если есть событие, которое может скажите мне, что MessageSendingOperations готов или нет.

+0

is stomp.subscribe выполняется после того, как дом готов? –

+0

@ ᴳᵁᴵᴰᴼ Да. это верно. Я вижу, что подписка msg была отправлена ​​из отладки Chrome для сетевого трафика websocket. Поэтому я не думаю, что это проблема с клиентской стороной. – user2001850

ответ

4

Вопрос, стоящий перед вами, лежит в природе clientInboundChannel, который по умолчанию является ExecutorSubscribableChannel.

Он имеет 3 subscribers:

0 = {[email protected]} "SimpleBroker[DefaultSubscriptionRegistry[cache[0 destination(s)], registry[0 sessions]]]" 
1 = {[email protected]} "UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]" 
2 = {[email protected]} "SimpAnnotationMethodMessageHandler[prefixes=[/app/]]" 

, которые вызываются в пределах taskExecutor, следовательно, асинхронно.

Первый здесь (SimpleBrokerMessageHandler (или StompBrokerRelayMessageHandler), если вы используете broker-relay) отвечает зарегистрировать subscription для topic.

Операция messagingTemplate.convertAndSend(/topic/path, "msg") может быть выполнена до регистрации подписки для этого сеанса WebSocket, поскольку они выполняются в отдельных потоках. Следовательно, обработчик Broker не знает, как отправить сообщение на сеанс.

@SubscribeMapping может быть сконфигурирован по методу с return, где результат этого метода будет отправлен в качестве ответа на то, что функция subscription на клиенте.

НТН

+0

внутри моего метода подписки, я асинхронно вызывал сервисный уровень, например subscriableService.registerAndHandleWith (new Handler() {}). Так что я не могу сразу вернуться в этот метод. Что бы вы порекомендовали в этом сценарии? Благодарю. – user2001850

+0

'Future.get()' или 'CountDownLatch' из этого' registerAndHandleWith'. С другой стороны вы можете получить доступ к 'SimpleBrokerMessageHandler' и заполнить какой-то свой собственный пользовательский метод' DefaultSubscriptionRegistry', чтобы переопределить 'addSubscriptionInternal', чтобы поднять некоторый пользовательский' ApplicationEvent', чтобы прослушать его из другого компонента, чтобы отправить это сообщение в тему, когда 'Подписка' будет уже там. Это для случая, когда вам действительно нужен асинхронный материал и не перегружать исполнятеля 'clientInboundChannel', чтобы ждать этого« Будущего ». –

+1

Еще раз спасибо Артем, я полагаю, что я не могу использовать существующий SessionSubscribeEvent, поскольку он только сообщает мне, что клиент запросил, но не означает, что регистрация подписки завершена (было бы неплохо иметь что-то вроде SessionSubscribe ** d * *Мероприятие). – user2001850

0

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

К сожалению, я не мог использовать метод возврата @SubscriptionMapping, потому что мы использовали настраиваемый объект mapper, который динамически изменялся в зависимости от типа пользователя (по существу, фильтрация атрибутов).

Я искал код Spring и нашел, что SubscriptionMethodReturnValueHandler несет ответственность за отправку возвращаемого значения подписных подписей и имел другой файл messagingTemplate, чем autowired SimpMessagingTemplate моего контроллера асинхронного вызова!

Итак, решение было autowiring MessageChannel clientOutboundChannel в моем контроллере async и с помощью этого для создания SimpMessagingTemplate.(Вы не можете напрямую подключить его, потому что вы просто получите шаблон для брокера).

В методах подписки я тогда использовал прямой шаблон, в то время как в других методах я использовал шаблон, который пошел брокеру.