"No decoder for session id" in logs for Spring web sockets and Stomp

Viewed 2741

I have a tomcat cluster with 2 instances and apache proxy in one server. Application use Spring framework 4.3.10 with web sockets, apache-activemq-5.15.0 as stomp broker:

<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>

Now about 20 clients connect to web-sockets in the same time. All works well, but periodically I have an error in logs (estimated 8-10 times in an hour). How can I fix it?

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)
2 Answers

It's solved in new spring framework version (https://github.com/spring-projects/spring-framework/commit/f425a993e7be82ffdbdda24370925a34c42925f2)

If you are using old version ( up to 2019 ), you can fix this problem by overriding 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");
        }
    
}

And use it in main 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);
        }
    });
}

Update the heartbeat header in the connect frame to 5000,5000 which means both client and server will need to send the heartbeat frame every frame 5 sec. In my case the server was dropping the connection when it was idle for too long. What this essentially does is keep the underlying TCP connection alive so that the server does not close it. Also restart the server after making this change. Hope this helps!!

If you are using a java client over amqp to connect to rabbitMq you can set

ConnectionFactory cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.setRequestedHeartbeat(60);

References:

https://www.rabbitmq.com/heartbeats.html

https://stomp.github.io/stomp-specification-1.2.html#CONNECT_or_STOMP_Frame

Related