2015-12-30 4 views
5

Java Springでサーバーサイドイベント(SSE)を使用しています。イベントは、私が実行し、クライアントに通知する必要があるときはいつでも、次にJava spring SseEmitter/ResponseBodyEmitter:ブラウザのリロードを検出します。

SseEmitter emitter = new SseEmitter(-1L); 
emitter.onCompletion(() -> { 
     logger.debug(TAG + "Emitter completed."); 
     emitters.remove(emitter); 
    }); 
return emitter; 

for (ResponseBodyEmitter emitter: emitters) { 
     emitter.send("Message #1"); 
} 

問題新しいクライアントは、私はRESTコントローラで次のコードを実行するイベントサービスに加入するたびにクライアントの1人がブラウザをリロードすると、エミッタが(期待通りに)完成せず、上のコードを呼び出すときにパイプの例外が壊れるということです。この例外がトリガーされた後でなければ、エミッターが完了していることがわかります。

この問題を解決する方法はありますか?

答えて

2

ブラウザがリロードされると、サーバーに新しいEventSourceが確立されます。あなたの問題は、クライアントのエンドポイントがもうない古いものにあります。

これは、同じクライアントに接続していることを検出し、明示的に古いエミッタでcompleteを呼び出すことをお勧めします。

私のケースでは、EventSourceがURLパラメータとして渡すトークンに基づいてこれを検出できます。新しく作成されたエミッタを「ユーザオブジェクト」にフックすると、ユーザフィールド変数に新しいものを割り当てる前に、以前のエミッタを完了するようにします。

コードからは、リストまたはエミッタのセットがあるようです。おそらくマップにする代わりに、クライアントのIDをキーにし、エミッタを値として持つことができますか?

すべてがアプリケーションに依存するため、正確にどの情報をクライアントIDとして使用するかはわかりません。私の場合、それはJWTトークンですが、おそらくクライアントの番号付けスキームを作成することができます。

+0

どのように複数のタブを扱っていますか?jwtはセッション内のすべてのタブで同じでしょうか? – TruckDriver

+0

クライアントが突然アプリケーションを終了した場合、30日間戻ってくることはありません。古い接続はまだ開いたままですか?複数のAppサーバーがロードバランスされている場合でも、これはうまく機能しません。ユーザーが再接続すると別のASに接続できるからです。これは有望なアプローチのように見えません。 – user1102532

+0

いいえ、コンストラクタにタイムアウト値を指定してSseEmitterを作成します。タイムアウトはアイドルとは関係ありませんが、「生きる時間」に似ています。設計上、接続は、例えば、 X秒/分であり、それでもクライアントが必要な場合は再作成します。ロードバランシングに関しては、もちろん、クライアントが接続または再接続するときに、サーバーコードはクライアントのセッションを再作成するためにメモリ内の状態に依存してはいけません。しかし、やはりSSEとSpringのSseEmitterの設計は本質的にセッション/接続中心であるため、接続が生存している限り、memで作業することができます。 –

関連する問題