2015-12-30 4 views
5

Я использую серверные события (SSE) в Java Spring. Всякий раз, когда новый клиент подписывается на услугу событий я выполнить следующий код в контроллере REST:Java spring SseEmitter/ResponseBodyEmitter: обнаружение перезагрузки браузера

SseEmitter emitter = new SseEmitter(-1L); 
emitter.onCompletion(() -> { 
     logger.debug(TAG + "Emitter completed."); 
     emitters.remove(emitter); 
    }); 
return emitter; 

Затем, когда должно быть доведено до сведения клиентов исполняют событие:

for (ResponseBodyEmitter emitter: emitters) { 
     emitter.send("Message #1"); 
} 

Проблемы заключается в том, что когда один из клиентов перезагружает браузер, эмиттер не завершается (как я и ожидал), и при вызове вышеприведенного кода я получаю исключение прерывистого канала. Только после запуска этого исключения я вижу, что эмиттер завершен.

Есть ли способ решить эту проблему?

ответ

2

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

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

В моем случае я могу обнаружить это на основе токена, который EventSource передает в качестве параметра URL. Когда я подключаю вновь созданный эмиттер к «объекту пользователя», я должен завершить предыдущий эмиттер перед назначением новой переменной поля пользователя.

Из вашего кода, похоже, у вас есть список или набор эмиттеров. Можете ли вы, возможно, сделать карту вместо нее, где у вас есть идентификатор клиента в качестве ключа, а эмиттер - как значение?

Я не могу точно рассказать вам, какую часть информации использовать как идентификатор клиента, поскольку все зависит от вашего приложения. В моем случае это токен JWT, но вы могли бы просто создать схему нумерации клиентов ...

+0

Как вы обрабатываете несколько вкладок, jwt будет одинаковым для всех вкладок в сеансе, правильно? – TruckDriver

+0

Что делать, если клиент внезапно закрывает приложение, никогда не возвращается в течение 30 дней. Старое соединение все равно останется открытым? Это также не очень хорошо работает, когда есть несколько серверов приложений с балансировкой нагрузки - поскольку пользователь может подключаться к другой AS при их повторном подключении. Это не похоже на перспективный подход. – user1102532

+0

Нет, SseEmitter создается со значением тайм-аута в конструкторе. Тайм-аут не относится к простоям, а скорее к «времени жить». По дизайну соединения будут удалены каждый, например. X секунд/минут, и клиент воссоздает его ... если он по-прежнему необходим. Что касается балансировки нагрузки, конечно, когда клиент подключается или повторно подключается, код сервера не должен полагаться на состояние в памяти, чтобы воссоздать сеанс клиента. Но опять же, дизайн SSE, а также SseEmitter от Spring, по своей сути связан с сеансом/соединением, так что пока соединение живое, вы можете работать в mem. –

Смежные вопросы