2016-03-30 22 views
2

私はServer Sent Eventsを初めて利用していますが、Springでは使用できません。 SSEEmitterを起動し、ループ内のループがUIに4秒ごとにメッセージを送信する別のスレッドに渡すUIのボタンからトリガーされるコントローラーを作成しました。 これまでのところ私はそれぞれ4秒間スリープする10のループを実行していますが、突然6または7ループの繰り返しの周りに例外が発生します。 "スレッドで例外" Thread-4 "java.lang.IllegalStateException:ResponseBodyEmitterが既に完了しています" ..Spring SSEEmitter途中完了

したがって、イベントソースは再び接続を再確立します。つまり、コントローラメソッドを再呼び出しします。これは確かに望ましくありません。

私はここで簡単なことをしようとしています。ユーザーはボタンをクリックして購読します。 サーバーは何時でもブラウザに10または20の応答を送信します。 SSEが作成したものだと思います。 .:

@RequestMapping("/subscribe") 
public SseEmitter subscribe() { 
    SseEmitter sseEmitter = new SseEmitter(); 
    try { 
     sseEmitter.send("Dapinder"); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    Runnable r = new AnotherThread(sseEmitter); 
    new Thread(r).start(); 
    return sseEmitter; 
} 

パブリッククラスAnotherThread以下

コード実装のRunnable {

private SseEmitter sseEmitter; 

public AnotherThread(SseEmitter sseEmitter) { 
    super(); 
    this.sseEmitter = sseEmitter; 
} 

@Override 
public void run() { 
    SseEventBuilder builder = SseEmitter.event(); 
    builder.name("dapEvent"); 
    for (int i = 0; i < 10; i++) { 
     builder.data("This is the data: " + i +" time."); 
     try { 
      //sseEmitter.send(builder); 
      sseEmitter.send("Data: "+i); 
      //sseEmitters.get(1L).send("Hello"); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     try { 
      Thread.sleep(4000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
    sseEmitter.complete(); 
} 

public SseEmitter getSseEmitter() { 
    return sseEmitter; 
} 

public void setSseEmitter(SseEmitter sseEmitter) { 
    this.sseEmitter = sseEmitter; 
} 

}

function start() { 
    var eventSource = new EventSource("http://localhost:8080/HTML5SSE/springSSE/subscribe"); // /springSSE/connect 
     eventSource.onmessage = function(event) { 
     document.getElementById('foo').innerHTML = event.data; 
    }; 

} 


<button onclick="start()">Subscribe</button> 
+0

このトピックを見てください:http://stackoverflow.com/questions/29880336/spring-sseemitter-causes-cannot-forward-after-response-has-been-committed-exceptそれは同じ問題のように見えます。 – ctranxuan

+0

私は同じ問題があります。私が別のスレッドでSseEmitterを送信する準備ができたら、タイムアウトまたはどこかで完了すると、それが古くなり、送信がIllegalStateExceptionをスローします。あなたは良い解決策を見つけましたか? –

答えて

2

あなたのビルダーが使用されていません。あなたはビルダーを作成して設定しますが、次に 'sseEmitter.send'を使って明示的なメッセージを直接送信します。これを試してみてください:

sseEmitter.send(SseEmitter.event().name("dapEvent").data("This " + i +" time."); 

さらにもう1つ:なぜサブスクライブ方式のsendメソッドを呼び出すのですか?現時点では、SseEmitterは返されていません。このメッセージはクライアントに届いていますか?

ここでは、JavaScriptの観点からSSEについて説明します(春ではありません)。excellent articleストリーム上でcloseを呼び出すことで、クライアントからイベントストリームを取り消すことができます。イベントリスナーでこれを組み合わせて、あなたが必要なものを持っている必要があります。

var source = new EventSource('...'); 
source.addEventListener('error', function(e) { 
    if (e.currentTarget.readyState == EventSource.CLOSED) { 
    // Connection was closed. 
    } else { 
    // Close it yourself 
    source.close(); 
    } 
}); 
source.addEventListener('message', function(e) { 
    console.log(e.data); 
}); 

注:記事はe.readyState言います、しかし私は、これは間違っていると思います。受信したオブジェクトeEventです。 のようにEventSourceオブジェクトを取得する必要があります。

1

の2番目のコンストラクタSceEmitterを使用する必要があります。これは、のロングタイムアウト引数をとります。以下のコードを参照してください -

@RequestMapping("/subscribe") 
public SseEmitter subscribe() { 
    SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE) // for maximum timeout 

以下は、このコンストラクタのJavaにドキュメントのコピーである -

/** 
* Create a SseEmitter with a custom timeout value. 
* <p>By default not set in which case the default configured in the MVC 
* Java Config or the MVC namespace is used, or if that's not set, then the 
* timeout depends on the default of the underlying server. 
* @param timeout timeout value in milliseconds 
* @since 4.2.2 
*/ 

は、私は、TomcatにおけるSSEの接続のデフォルトのタイムアウトは40秒だと思います。確かに。

関連する問題