Spring Websocket Session共享解決思路

 Spring Websocket Session共享解決思路(背景)

 

Websocket Session 共享?好多人想都不想,直接上來丟  Redis  、  Memcache  等等?常用的HttpSession 共享如下圖

都是採用序列化和反序列化來實現,而Websocket Session  當你存儲的時候會發現出現這樣的問題。


 
  1. java.io.NotSerializableException: org.springframework.web.socket.adapter.standard.StandardWebSocketSession

在知乎上看到討論這個問題,摘抄一下。

 Websocket的長連接通道一但關閉了websocket session就失效了。不像http協議每次用就去新建一個連接。你是可以知道是哪個用戶,也知道要發哪些數據,但是問題在於。連接關閉後你發不過去。這也是websocketsession不能序列化的原因。自己構建的session不能解決長連接的問題。

而我的觀點。

我覺得 Websocket session 不能序列化的原因不應該在此,關閉失效,我們有失效調用的close方法,這個時候我從共享池(cache)中remove即可,只要cache裏有,那麼我就可以針對用戶發送請求,而且還有一個很現實的問題,我幾個節點運行的時候,輪詢的時候,從A點切換到B點的時候,用戶就不認識了。而且每個節點只知道每個節點的websocketSession,這設計太扯了。

事實證明我的觀點還是有遺漏,問題出在,上面引用的描述,它不像Http協議每次用就去新建一個  Session  。而是前端  JS  創建  Websocket  創建鏈接,就一個Websocket Session 的生命週期開啓。當一段時間不使用的時候,Websocket會主動關閉。如下圖。

再次使用會出現“WebSocket is already in CLOSING or CLOSED state.” 。所以在再次使用的時候,需要重新開啓一個Websocket Session ,而這個開啓,是有前端 JS 來完成的。

所以這個處理很簡單,需要增加判斷,或者try...catch 來完成重新創建。

 

Websocket Session 共享解決(方案一)

既然Websocket Session 不能直接序列化然後存儲,而且如果用戶請求的時候,開始與集羣中的A 節點創建鏈接,就算把這個  Session  拿到B 節點去再給用戶Push 消息,應該也是不通(沒測試)的。

所以我的解決方案採用訂閱式隊列去觸發  Websocket  反推的方式來實現看上去是  Session共享  的,思路如下圖:

Websocket Session  共享 思路圖解釋:

模塊一:這裏沒有什麼好說明的,因爲每一個用戶只創建以一個  Session  ,當用戶再次鏈接到第二個節點的時候,第一個節點的  Session  就會關閉和銷燬。而每個節點採用靜態(static )的成員變量去存儲(Map )當前節點的所有Websocket Session

模塊二:因爲每個節點存儲的是每個節點的所有Websocket Session ,那麼只需要判斷下當前節點是否存在當前用戶的Websocket Session ,存在則反推消息。

模塊三:當有消息需要通過  Websocket  推送給對應的用戶的時候,而又不知道這個用戶在哪個節點,那麼通過任務調度模塊,把消息以訂閱MQ 的方式放到  MQ  中,這樣每個節點都可以收到消息,從而參照 “模塊二” 判斷當前用戶是否存在當前節點。然後推送。

總結:目前我採用這種方式實現,沒什麼問題,但是是不是覺得太複雜了。是的,太重了,所以不推薦這樣去解決。

Websocket Session 共享解決(方案二)

其實我們之所以要做共享,其實就是因爲在集羣環境中,在HA/Nginx等方案中,做輪詢請求來減輕節點壓力,這樣導致用戶可能分配在多個節點,那麼如果我們採用IP等的Hash值規則,或者其他可以參考的規則。解決一個用戶只會分配到一個節點上,那麼就不存在Session共享了。這樣的中間件好多。如我們的“傘神”提出的 採用阿里的 Tenginesession sticky 組件” 就可以解決。其他的也很多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章