SpringBoot 生產級 WebSocket 集羣實踐,支持 10 萬連接!

1、問題背景

智慧門診系統旨在從一定程度上解決患者面臨的三長一短(掛號、看病、取藥時間長,醫生問診時間短)的問題。實現 “診前、診中、診後” 實時智能一體化,整合完善醫院工作流程。圍繞門診看病的各個環節,讓患者全程手機有提醒,讓患者少排隊、少跑腿、看病更簡單,獲得全流程的陪伴服務從而有效提升就醫體驗。

系統通過接收醫院第三方系統推送的門診數據,再結合業務服務處理後主動推送到前端,從而實時的將數據同步給患者手機。之所以沒有采用傳統的前端輪訓方案,主要是在當前業務場景下存在時效性不足,資源浪費等問題。但與此同時也有代價的,相比於 Http 的無狀態通信,服務端主動推送是有狀態協議的,客戶端連接服務器時只和集羣中一個節點連接,數據傳輸過程中也只與這一節點通信,在集羣多臺服務器環境下,我們就出現了服務端部分消息推送丟失的現象。

當前架構圖如下:

2、問題分析和整體思路

客戶端和服務端每次建立連接時候,會創建有狀態的會話 Session,服務器得保存維持連接的 Session。客戶端每次只能和集羣服務器其中的一個服務器連接,後續也是和該服務器進行數據傳輸。因此集羣的問題,應該考慮 Session 的問題,客戶端成功連接服務器之後,其他服務器也知道客戶端連接成功。

可以使用 Nginx 負載均衡的 ip hash 算法,客戶端每次都是請求同一個服務器,客戶端的 session 都保存在該服務器上,而後續請求都是請求該服務器,都能獲取到 session,就不存在分佈式 session 問題了。websocket 相對 http 來說,可以由服務端主動推動消息給客戶端,如果接收消息的服務端和發送消息消息的服務端不是同一個服務端,發送消息的服務端無法找到接收消息對應的 session,即兩個 session 不處於同一個服務端,也就無法推送消息。

解決問題的方法是將所有消息的發送方和接收方都處於同一個服務器下,而消息發送方和接收方都是不確定的,顯然是無法實現的。將消息的發送方和接收方都處於同一個服務器下才能發送消息,那麼可以轉換一下思路,可以將消息以消息廣播的方式通知給所有的服務器,可以使用消息中間件發佈訂閱模式,消息脫離了服務器的限制,通過發送到中間件,再發送給訂閱的服務器,類似廣播一樣,只要訂閱了消息,都能接收到消息的通知。

3、解決方案

WebSocket 協議是基於 TCP 的一種新的網絡協議,是一個應用層協議,是 HTML5 提供的一種在單個 TCP 連接上進行全雙工通訊的協議,與 TCP 一樣,客戶端和服務器都可以隨時向對方發送數據,而不用像 HTTP 請求 - 應答”通信模式。於是,服務器就可以變得更加 “主動” 了。一旦後臺有新的數據,就可以立即 “推送” 給客戶端,不需要客戶端輪詢,“實時通信”的效率也就提高了。

瀏覽器是一個 “沙盒” 環境,有很多的限制,不允許建立 TCP 連接收發數據,而 WebSocket 利用了 HTTP 本身的 “協議升級” 特性,“僞裝”成 HTTP,這樣就能繞過瀏覽器沙盒、與服務器直接建立“TCP 連接”,獲得更多的自由。

一個典型的 Websocket 握手請求如下:

1、客戶端請求

2、服務器迴應

WebSocket 是有狀態的,無法像直接 HTTP 以集羣方式實現負載均衡,長連接建立後即與服務端某個節點保持着會話,因此集羣下想要得知會話屬於哪個節點,有兩種方案,一種是使用類似微服務的註冊中心來維護全局的會話映射關係,一種是使用事件廣播由各節點自行判斷是否持有會話,兩種方案對比如表所示。

綜合考慮實現成本與集羣規模,選擇了輕量級的事件廣播方案。實現廣播可以選擇基於 RocketMQ 的消息廣播、基於 Redis 的 Publish/Subscribe、基於服務的通知等方案,其優缺點對比如表所示。從實時性、實現難易等方面考慮,同時對於持久化高可靠級別並沒有太高要求,最終選擇了 Redis。

改造後架構圖如下:

4、核心實現

基於 spring boot 建立 websocket 連接

基於 spring boot 接收 websocket 消息

基於 spring boot 發佈和訂閱 Redis 消息

vue 前端 websocket 建立連接、心跳檢測、發送消息、消息訂閱等

Nginx 反向代理配置

5、性能測試

性能壓測選擇兩臺配置爲 2 核 16G 的虛擬機,分別作爲服務器和客戶端。壓測時選擇爲網關開放了 5 個端口,同時建立 5 個客戶端,每個客戶端使用一個服務端端口建立起 2 萬連接,可以同時創建 10 萬個連接。連接數與內存使用情況如圖所示。

給 10 萬個長連接同時發送一條消息,採用單線程發送,服務器發送完成的平均耗時在 10s 左右,如圖所示。

當前的性能指標已滿足智慧門診的實際業務場景,可支持未來的業務增長。

6、產品效果

7、問題和展望

當前 WebSocket 實現分散在在各個服務中,與業務系統強耦合,如果有其他業務需要集成 WebSocket,面臨着重複開發的窘境,浪費成本、效率低下。後續建議在網關中擴展統一集成管理 websocket,能夠具備以下特點:

  1. 集中實現長連接管理和推送能力。統一技術棧,將長連接作爲基礎能力沉澱,便於功能迭代和升級維護。

  2. 與業務解耦。將業務邏輯與長連接通信分離,使業務系統不再關心通信細節,也避免了重複開發,浪費研發成本。

  3. 使用簡單。提供 HTTP 推送通道,方便各種開發語言的接入。業務系統只需要簡單的調用,就可以實現數據推送,提升研發效率。

  4. 分佈式架構。實現多節點的集羣,支持水平擴展應對業務增長帶來的挑戰;節點宕機不影響服務整體可用性,保證高可靠。

  5. 多端消息同步。允許用戶使用多個瀏覽器或標籤頁同時登陸在線,保證消息同步發送。

  6. 多維度監控與報警。自定義監控指標與現有微服務監控系統打通,出現問題時可及時報警,保證服務的穩定性。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/SDiD9KsxXTzL7P-Hl079eg