SpringBoot生產級WebSocket集群實踐,支持10萬連接!
一、問題背景
智慧門診系統旨在從一定程度上解決患者面臨的三長一短(掛號、看病、取藥時間長,醫生問診時間短)的問題。實現“診前、診中、診后”實時智能一體化,整合完善醫院工作流程。圍繞門診看病的各個環節,讓患者全程手機有提醒,讓患者少排隊、少跑腿、看病更簡單,獲得全流程的陪伴服務從而有效提升就醫體驗。
系統通過接收醫院第三方系統推送的門診數據,再結合業務服務處理后主動推送到前端,從而實時的將數據同步給患者手機。之所以沒有采用傳統的前端輪訓方案,主要是在當前業務場景下存在時效性不足,資源浪費等問題。但與此同時也有代價的,相比于Http的無狀態通信,服務端主動推送是有狀態協議的,客戶端連接服務器時只和集群中一個節點連接,數據傳輸過程中也只與這一節點通信,在集群多臺服務器環境下,我們就出現了服務端部分消息推送丟失的現象。
當前架構圖如下:
圖片
二、問題分析和整體思路
客戶端和服務端每次建立連接時候,會創建有狀態的會話Session,服務器得保存維持連接的Session。客戶端每次只能和集群服務器其中的一個服務器連接,后續也是和該服務器進行數據傳輸。因此集群的問題,應該考慮Session的問題,客戶端成功連接服務器之后,其他服務器也知道客戶端連接成功。
可以使用Nginx負載均衡的ip hash算法,客戶端每次都是請求同一個服務器,客戶端的session都保存在該服務器上,而后續請求都是請求該服務器,都能獲取到session,就不存在分布式session問題了。websocket相對http來說,可以由服務端主動推動消息給客戶端,如果接收消息的服務端和發送消息消息的服務端不是同一個服務端,發送消息的服務端無法找到接收消息對應的session,即兩個session不處于同一個服務端,也就無法推送消息。
解決問題的方法是將所有消息的發送方和接收方都處于同一個服務器下,而消息發送方和接收方都是不確定的,顯然是無法實現的。將消息的發送方和接收方都處于同一個服務器下才能發送消息,那么可以轉換一下思路,可以將消息以消息廣播的方式通知給所有的服務器,可以使用消息中間件發布訂閱模式,消息脫離了服務器的限制,通過發送到中間件,再發送給訂閱的服務器,類似廣播一樣,只要訂閱了消息,都能接收到消息的通知。
三、解決方案
WebSocket協議是基于TCP的一種新的網絡協議,是一個應用層協議,是 HTML5 提供的一種在單個 TCP 連接上進行全雙工通訊的協議,與 TCP 一樣,客戶端和服務器都可以隨時向對方發送數據,而不用像 HTTP請求 - 應答”通信模式。于是,服務器就可以變得更加“主動”了。一旦后臺有新的數據,就可以立即“推送”給客戶端,不需要客戶端輪詢,“實時通信”的效率也就提高了。
瀏覽器是一個“沙盒”環境,有很多的限制,不允許建立 TCP 連接收發數據,而WebSocket利用了 HTTP 本身的“協議升級”特性,“偽裝”成 HTTP,這樣就能繞過瀏覽器沙盒、與服務器直接建立“TCP 連接”,獲得更多的自由。
一個典型的 Websocket 握手請求如下:
1.客戶端請求
圖片
2.服務器回應
圖片
WebSocket是有狀態的,無法像直接HTTP以集群方式實現負載均衡,長連接建立后即與服務端某個節點保持著會話,因此集群下想要得知會話屬于哪個節點,有兩種方案,一種是使用類似微服務的注冊中心來維護全局的會話映射關系,一種是使用事件廣播由各節點自行判斷是否持有會話,兩種方案對比如表所示。
圖片
綜合考慮實現成本與集群規模,選擇了輕量級的事件廣播方案。實現廣播可以選擇基于RocketMQ的消息廣播、基于Redis的Publish/Subscribe、基于服務的通知等方案,其優缺點對比如表所示。從實時性、實現難易等方面考慮,同時對于持久化高可靠級別并沒有太高要求,最終選擇了Redis。
圖片
改造后架構圖如下:
圖片
四、核心實現
基于spring boot建立websocket連接
圖片
基于spring boot接收 websocket消息
圖片
基于spring boot發布和訂閱Redis消息
圖片
vue前端websocket建立連接、心跳檢測、發送消息、消息訂閱等
圖片
圖片
圖片
Nginx反向代理配置:
五、性能測試
性能壓測選擇兩臺配置為2核16G的虛擬機,分別作為服務器和客戶端。壓測時選擇為網關開放了5個端口,同時建立5個客戶端,每個客戶端使用一個服務端端口建立起2萬連接,可以同時創建10萬個連接。連接數與內存使用情況如圖所示。
圖片
給10萬個長連接同時發送一條消息,采用單線程發送,服務器發送完成的平均耗時在10s左右,如圖所示。
圖片
當前的性能指標已滿足智慧門診的實際業務場景,可支持未來的業務增長。
六、產品效果
圖片
七、問題和展望
當前WebSocket實現分散在在各個服務中,與業務系統強耦合,如果有其他業務需要集成WebSocket,面臨著重復開發的窘境,浪費成本、效率低下。后續建議在網關中擴展統一集成管理websocket,能夠具備以下特點:
- 集中實現長連接管理和推送能力。統一技術棧,將長連接作為基礎能力沉淀,便于功能迭代和升級維護。
- 與業務解耦。將業務邏輯與長連接通信分離,使業務系統不再關心通信細節,也避免了重復開發,浪費研發成本。
- 使用簡單。提供HTTP推送通道,方便各種開發語言的接入。業務系統只需要簡單的調用,就可以實現數據推送,提升研發效率。
- 分布式架構。實現多節點的集群,支持水平擴展應對業務增長帶來的挑戰;節點宕機不影響服務整體可用性,保證高可靠。
- 多端消息同步。允許用戶使用多個瀏覽器或標簽頁同時登陸在線,保證消息同步發送。
- 多維度監控與報警。自定義監控指標與現有微服務監控系統打通,出現問題時可及時報警,保證服務的穩定性。