web-dev-qa-db-ja.com

ハートビートをスケジュールしないWebSocket上のSpringSTOMP

CONNECTフレームを渡すSpringoverWebSockets接続があります。

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

ハンドラーが確認し、新しいセッションを開始して、次を返します。

CONNECTED
version:1.2
heart-beat:0,0

ただし、WebSocketを開いたままにできるように、ハートビートが必要です。私たちはSockJSを使用していません

Springメッセージハンドラーをステップスルーしました。

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デバッグを実行しているときに、「ハートビートをスケジュールしています...」などの通知が表示されましたが、現在は表示されていませんか?

何が起こっているのか分かりますか?

ありがとう

ドキュメント で説明を見つけました:

ハートビートの送信に使用されるSockJSタスクスケジューラのスレッドプールからのSockJSタスクスケジューラの統計。 ハートビートがSTOMPレベルでネゴシエートされると、SockJSハートビートが無効になることに注意してください。

SockJSの心拍数はSTOMPの心拍数とは異なりますか?

10
jordan.baucke

はいSockJSのハートビートは異なります。基本的に同じことですが、SockJSプロトコルでの目的は、接続が「デッド」のように見えないようにすることです。その場合、プロキシはプロアクティブに接続を閉じることができます。より一般的には、ハートビートにより、各サイドは接続の問題をプロアクティブに検出し、リソースをクリーンアップできます。

トランスポート層で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(...);
    }

}
7

Spring 4.2以降、組み込みのSimpleBrokerを備えたStomp over SockJSを使用して、サーバー側からハートビートネゴシエーションの結果を完全に制御できます。

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();
    }
}
11
artemisian

Spring、Websocket、STOMP、Springセッションでも同じ問題が発生しました。WebSocketがサーバー側でメッセージを受信しない間、ハートビートやSpringセッションが期限切れになることはありません。最終的に、ブラウザから20000ミリ秒ごとにSTOMPハートビートを有効にし、SpringsessionRepositoryInterceptorの一致にSimpMessageType.HEARTBEATを追加して、Springセッションの最終アクセスを維持しました。メッセージなしのSTOMPハートビートで更新された時間。インビルドSpringセッションとWebSocketセッションを有効にするためのベースとして、AbstractSessionWebSocketMessageBrokerConfigurerを使用する必要がありましたバインディング。 春のマニュアルの例。公式の例では、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});
   }
}

私たちが試したもう1つの方法は、SessionRepositoryMessageInterceptor機能を再実装して、outboundWebSocketメッセージのSpringセッションの最終アクセス時間を更新し、WebSocketを維持することです。 session->リスナーを介したSpringセッションマップですが、上記のコードでうまくいきました。

7
Alex Vorona