Как быстро закрыть невосприимчивый websocket в Java Spring Tomcat?
У меня есть приложение реального времени с клиентами, использующее websockets для подключения к серверу Spring Framework, на котором запущен Spring Boot Tomcat. Я хочу, чтобы сервер быстро (в течение 5 секунд) обнаружил, когда клиент перестает отвечать из-за отключения сети или другой проблемы и закрывает веб-узел.
Я пробовал
-
Установка максимального тайм-аута ожидания сеанса, как описано в документации как "Настройка механизма WebSocket",
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
@Bean
public WebSocketHandler clientHandler() {
return new PerConnectionWebSocketHandler(ClientHandler.class);
}
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container =
new ServletServerContainerFactoryBean();
container.setMaxSessionIdleTimeout(5000);
container.setAsyncSendTimeout(5000);
return container;
}
Я не уверен, что это реализовано правильно, потому что я не вижу связи между ServletServerContainerFactoryBean и моим поколением ClientHandlers.
-
Отправка сообщений ping с сервера каждые 2,5 секунды. После того, как я вручную отключу клиента, нарушив сетевое подключение, сервер с радостью отправит пинги еще на 30 секунд, пока не появится ошибка транспорта.
-
1 и 2 одновременно
-
1 и 2 и установка server.session-timeout = 5
в application.properties
Моя методология для тестирования:
- Подключите веб-узел с ноутбука к серверу Tomcat.
- Отключите сетевое подключение на ноутбуке с помощью физического переключателя
- Ожидание событий сервера Tomcat
Как сервер Spring быстро обнаруживает, что клиент был отключен или не отвечает на закрытие websocket?
Ответы
Ответ 1
Подход, который я в конечном итоге принял, заключался в реализации протокола ping-pong на уровне приложения.
- Сервер отправляет клиенту сообщение ping с периодом
p
.
- Клиент отвечает на каждое сообщение ping сообщением pong.
- Если сервер отправляет более
n
пинговые сообщения без получения ответа на понг, он генерирует событие таймаута.
- Клиент также может генерировать событие таймаута, если он не получает сообщение ping в
n*p
.
Должен быть гораздо более простой способ реализовать это с использованием тайм-аутов в базовом TCP-соединении.
Ответ 2
События приложений могут вам помочь.
PS: События, связанные с аннотацией
PS2: я сделал примерный проект для вас
Ответ 3
ServletServerContainerFactoryBean просто настраивает базовый JSR-356 WebSocketContainer через конфигурацию Spring при запуске. Если вы заглянете внутрь, вы увидите его тривиальным.
Из того, что я могу видеть в коде Tomcat о обработке maxSessionIdleTimeout, метод WFWWWWFWW запускается каждые 10 секунд по умолчанию, чтобы узнать, есть ли истекшие сеансы.
Я подозреваю, что пинги, которые вы отправляете с сервера, заставляют сеанс работать активным, следовательно, не помогают в отношении конфигурации тайм-аута в режиме ожидания.
Что касается того, почему Tomcat не понимает, что клиент отключен раньше, я не могу сказать. По моему опыту, если клиент закрывает соединение WebSocket или если я убил браузер, он сразу обнаружил. В любом случае это больше связано с Tomcat не Spring.