WebSockets上的Spring STOMP没有安排心跳

时间:2016-08-30 06:52:43

标签: stomp spring-websocket

我们有一个Spring over WebSockets连接,我们正在传递CONNECT帧:

CONNECT\naccept-version:1.2\nheart-beat:10000,10000\n\n\u0000

处理程序确认,启动一个新会话,然后返回:

CONNECTED
version:1.2
heart-beat:0,0

然而,我们想要心跳,以便我们可以保持WebSocket开放。我们使用SockJS。

我介绍了Spring Message Handler:

StompHeaderAccessor [headers={simpMessageType=CONNECT, stompCommand=CONNECT, nativeHeaders={accept-version=[1.2], heart-beat=[5000,0]}, simpSessionAttributes={}, simpHeartbeat=[J@5eba717, simpSessionId=46e855c9}]

获得heart-beat(本机标头)后,它会设置内存地址simpHeartbeat=[J@5eba717, simpSessionId=46e855c9}]

值得注意的是,经纪人验证后:

Processing CONNECT session=46e855c9(这里的sessionId与simpSessionId不同)?

当运行较早的TRACE调试时,我看到了一个通知“调度心跳......”或者其他类似的东西......虽然我现在还没有看到它?

知道发生了什么事吗?

由于

我在documentation中找到了解释:

  

SockJS任务的线程池中的SockJS任务计划程序统计信息   用于发送心跳的调度程序。 注意心跳时   在STOMP级别协商SockJS心跳被禁用。

SockJS心跳是否与STOMP心跳不同?

3 个答案:

答案 0 :(得分:10)

启动Spring 4.2,您可以使用Stomp over SockJS和内置的SimpleBroker从服务器端完全控制心跳协商结果:

public class WebSocketConfigurer extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
        te.setPoolSize(1);
        te.setThreadNamePrefix("wss-heartbeat-thread-");
        te.initialize();

        config.enableSimpleBroker("/")
                /**
                 * Configure the value for the heartbeat settings. The first number
                 * represents how often the server will write or send a heartbeat.
                 * The second is how often the client should write. 0 means no heartbeats.
                 * <p>By default this is set to "0, 0" unless the {@link #setTaskScheduler
                 * taskScheduler} in which case the default becomes "10000,10000"
                 * (in milliseconds).
                 * @since 4.2
                 */
                .setHeartbeatValue(new long[]{heartbeatServer, heartbeatClient})
                .setTaskScheduler(te);
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint(.....)
                .setAllowedOrigins(....)
                .withSockJS();
    }
}

答案 1 :(得分:6)

是的SockJS心跳不同。基本上相同的事情,但他们在SockJS协议中的目的是确保连接看起来不像它已经死了&#34;在这种情况下,代理可以主动关闭它。更一般地,心跳允许每一方主动地检测连接问题并清理资源。

在传输层使用STOMP和SockJS时,如果正在使用STOMP心跳,则不需要同时关闭SockJS心跳的原因。但是你在这里没有使用SockJS。

您没有显示任何配置,但我的猜测是您正在使用不会自动发送心跳的内置简单代理。配置时,您将看到启用心跳的选项,还需要设置任务计划程序。

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
         // ...
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableStompBrokerRelay(...)
                .setTaskScheduler(...)
                .setHeartbeat(...);
    }

}

答案 2 :(得分:5)

我们遇到了与Spring,Websockets,STOMP和Spring Sessions相同的问题 - 没有心跳和Spring会话可能会过期而websocket在服务器端没有收到消息。我们最终每隔20000ms从浏览器启用STOMP心跳,并将 SimpMessageType.HEARTBEAT 添加到Spring sessionRepositoryInterceptor 匹配,以保持Spring会话上次访问时间在STOMP心跳上更新,而不显示消息。我们必须使用 Abstract Session WebSocketMessageBrokerConfigurer 作为基础来启用内置Spring会话和websocket会话绑定。 Spring manual第二个示例。在官方示例中,Spring会话更新入站websocket CONNECT / MESSAGE / SUBSCRIBE / UNSUBSCRIBE消息,但不是心跳,这就是为什么我们需要重新配置2件事 - 至少启用入站心跳并调整Spring会话以响应websocket心跳

public class WebSocketConfig extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> {

   @Autowired
   SessionRepositoryMessageInterceptor sessionRepositoryInterceptor;

   @Override
   public void configureMessageBroker(MessageBrokerRegistry config) {
       sessionRepositoryInterceptor.setMatchingMessageTypes(EnumSet.of(SimpMessageType.CONNECT,
               SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE,
               SimpMessageType.UNSUBSCRIBE, SimpMessageType.HEARTBEAT));

       config.setApplicationDestinationPrefixes(...);
       config.enableSimpleBroker(...)
             .setTaskScheduler(new DefaultManagedTaskScheduler())
             .setHeartbeatValue(new long[]{0,20000});
   }
}

我们尝试的另一种方法是重新实现 SessionRepositoryMessageInterceptor 功能,以便在出站 websocket消息上更新Spring会话的最后访问时间,并维护websocket session-&gt; Spring会话映射通过听众,但上面的代码做了伎俩。