我有一个tomcat集群,在一个服务器中有2个实例和apache代理。应用程序使用带有Web套接字的Spring框架4.3.10,apache-activemq-5.15.0作为stomp代理:
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/wshandler" allowed-origins="*">
</websocket:stomp-endpoint>
<websocket:stomp-broker-relay prefix="/topic,/queue"
relay-host="localhost" relay-port="62356"
heartbeat-send-interval="10000" heartbeat-receive-interval="10000"/>
<websocket:client-inbound-channel>
<websocket:interceptors>
<bean class="somepath.TopicSubscriptionInterceptor"/>
</websocket:interceptors>
</websocket:client-inbound-channel>
</websocket:message-broker>
现在大约有20个客户端同时连接到Web套接字。一切运行良好,但我会定期在日志中出错(估计每小时8-10次)。我该如何解决?
2017-10-06 09:54:01,046 ERROR [StompSubProtocolHandler] Failed to parse TextMessage payload=[], byteCount=1, last=true] in session 6f. Sending STOMP ERROR to client.
java.lang.IllegalStateException: No decoder for session id '6f'
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:249)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:307)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:110)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:78)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:394)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
答案 0 :(得分:2)
已在新的 spring 框架版本 (https://github.com/spring-projects/spring-framework/commit/f425a993e7be82ffdbdda24370925a34c42925f2) 中解决
如果您使用的是旧版本(直到 2019 年),您可以通过覆盖 WebSocketHandlerDecorator 来解决此问题:
@Slf4j
public class WebSocketSessionCapturingHandlerDecorator extends WebSocketHandlerDecorator {
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
if(session.isOpen()){
super.handleMessage(session, message);
}else{
log.info("Dropped inbound WebSocket message due to closed session");
}
}
并在主 WebSocketConfig 中使用它:
@Slf4j
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
@Override
public WebSocketHandler decorate(WebSocketHandler webSocketHandler) {
return new WebSocketSessionCapturingHandlerDecorator(webSocketHandler);
}
});
}
答案 1 :(得分:1)
将连接帧中的心跳头更新为5000,5000,这意味着客户端和服务器都需要每5秒钟发送一次心跳帧。在我的情况下,服务器在空闲时间太长时断开了连接。这实际上是使基础TCP连接保持活动状态,以便服务器不会关闭它。进行此更改后,还要重新启动服务器。 希望这会有所帮助!
如果您通过amqp使用Java客户端连接到RabbitMq,则可以设置
ConnectionFactory cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.setRequestedHeartbeat(60);
参考文献:
https://www.rabbitmq.com/heartbeats.html
https://stomp.github.io/stomp-specification-1.2.html#CONNECT_or_STOMP_Frame