2015-12-30 52 views
5

我在Java Spring中使用服務器端事件(SSE)。每當一個新的客戶端訂閱事件服務,我在REST控制器執行以下代碼: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"); 
} 

問題當其中一個客戶端重新加載瀏覽器時,發射器沒有完成(正如我預期的那樣),並且在調用上面的代碼時出現斷開的管道異常。只有在這個異常被觸發後,我纔看到發射器已經完成。

有沒有辦法解決這個問題?

回答

2

當瀏覽器重新加載時,它會爲您的服務器建立一個新的EventSource,對吧?你的問題是與舊的,沒有客戶端端點了。

我建議你嘗試檢測它是連接的客戶端,然後在舊的發射器上明確地調用完成。

在我的情況下,我可以基於EventSource作爲URL參數傳入的令牌來檢測此情況。當我將新創建的發射器掛接到「用戶對象」時,我確保在將新分配給用戶字段變量之前完成之前的發射器。

從你的代碼看來,你似乎有一個或一組發射器。你可以用它作爲地圖,而你有一些客戶端標識作爲關鍵字,而發射器作爲值?

我無法確切地告訴您使用哪種信息作爲客戶端身份,因爲這一切都取決於您的應用程序。在我的情況下,它是一個JWT令牌,但你也許可以簡單地創建一個客戶端編號方案...

+0

你如何處理多個選項卡,jwt對於會話中的所有選項卡都是相同的,對嗎? – TruckDriver

+0

如果客戶突然關閉應用程序,30天內不再回來,舊連接仍然保持打開狀態?當多個App服務器負載均衡時,這也不能很好地工作 - 因爲用戶在重新連接時可以連接到不同的AS。這似乎不是一個有希望的方法。 – user1102532

+0

不,使用構造函數中的超時值創建SseEmitter。超時不是閒置,而更像是「生存時間」。通過設計,每個連接將被丟棄。 X秒/分鐘,客戶將重新創建它......如果仍然需要的話。關於負載均衡,當客戶端連接或重新連接時,服務器代碼不能依賴內存中的狀態來重新創建客戶端的會話。但是,SSE的設計以及Spring的SseEmitter的設計本質上是以會話/連接爲中心的,只要連接處於活動狀態,就可以在mem中工作。 –