Spring Web 套接字和 Stomp 的日志中的“会话 id 没有解码器”

Posted

技术标签:

【中文标题】Spring Web 套接字和 Stomp 的日志中的“会话 id 没有解码器”【英文标题】:"No decoder for session id" in logs for Spring web sockets and Stomp 【发布时间】:2017-10-06 10:45:34 【问题描述】:

我在一台服务器上有一个带有 2 个实例和 apache 代理的 tomcat 集群。应用程序使用带有 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 个客户端同时连接到网络套接字。一切正常,但我定期在日志中有错误(估计一小时内有 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)

【问题讨论】:

在activemq日志中有这个:>>2017-10-07 18:02:40,008 |警告 |传输连接到:tcp://127.0.0.1:52482 失败:org.apache.activemq.transport.InactivityIOException:通道太长时间处于非活动状态(> 10000):tcp://127.0.0.1:52482 | org.apache.activemq.broker.TransportConnection.Transport | ActiveMQ InactivityMonitor Worker 想想,就是这个原因 似乎与客户端的会话已关闭,这删除了与会话 ID 关联的解码器;如果之后在此会话中收到任何消息,则抛出“没有会话解码器”。也许您可以尝试增加heartbeat-receive-interval? AMQP 发送心跳的频率不够,Spring 中继掉线? 【参考方案1】:

在新的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);
        
    );

【讨论】:

【参考方案2】:

将连接帧中的心跳帧更新为 5000,5000,这意味着客户端和服务器都需要每帧 5 秒发送一次心跳帧。在我的情况下,服务器在空闲时间过长时会断开连接。这实质上所做的是保持底层 TCP 连接处于活动状态,以便服务器不会关闭它。进行此更改后还要重新启动服务器。 希望这会有所帮助!

如果您使用 java 客户端通过 amqp 连接到 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

【讨论】:

您得到 no decoder for id 错误,因为服务器关闭了连接,并且 spring 丢弃了会话以及与之关联的所有内容(解码器),因此出现了错误。要修复它,您需要确保底层 TCP 连接始终保持活动状态。您可以通过设置心跳来做到这一点,心跳只是客户端和服务器之间来回发送的 ping,这样可以保持连接处于活动状态。您只需将某些标头传递给服务器,其余的由服务器完成。查看 STOMP 文档。

以上是关于Spring Web 套接字和 Stomp 的日志中的“会话 id 没有解码器”的主要内容,如果未能解决你的问题,请参考以下文章

春天引导+的Spring Web插槽+ RabbitMQ的STOMP网站

Spring 4 websocket + stomp + rabbitmq 和集群

基于 Spring STOMP 令牌的安全性

没有 SockJS/StompJS 的 Spring STOMP 服务器

Spring WebSockets 运行时监控 STOMP 帧 - 解释

如何使用Spring-boot处理越来越多的STOMP主题