HTTP協議是一種無狀態的、基於TCP的請求/響應模式的協議,請求只能由客戶端發起、服務端進行響應。在大多數場景,這種請求/響應的Pull模式已經可以滿足需求。但在某些情形,例如訊息推送、通知等應用場景,需要實時將資料同步到客戶端,這就要求服務端支援主動Push資料。
服務端推送技術歷史悠久,經歷了短輪詢、長輪詢的發展,一定程度上能夠解決問題,但也存在著不足,例如時效性、資源浪費等。HTML5標準帶來的WebSocket規範基本結束了這一局面,成為目前服務端推送技術的主流方案。
在系統中整合WebSocket十分簡單,相關討論與資料很豐富。但如何實現一個通用的WebSocket推送閘道器尚未有成熟的方案。目前的雲服務廠商主要關注iOS和安卓等移動端推送,也缺少對WebSocket的支援。本文介紹了我們基於Netty實現WebSocket長連線閘道器時的一些思考和經驗。
1 愛奇藝號WebSocket使用現狀愛奇藝號是愛奇藝內容創作、分發和變現的平臺,涵蓋自媒體、網大、網劇、兒童、知識、紀錄片等多個業務,是愛奇藝內容生態的重要組成。愛奇藝號作為前臺系統,對使用者體驗有較高要求,直接影響著創作者的創作熱情。目前,愛奇藝號有多個業務場景中用到了WebSocket推送技術,包括:
使用者評論。實時的將評論訊息推送到瀏覽器。實名認證。合同簽署前需要對使用者進行實名認證,使用者掃描二維碼後進入第三方的認證頁面,認證完成後非同步通知瀏覽器認證的狀態。活體識別。類似實名認證,當活體識別完成後,非同步將結果通知瀏覽器。在實際的業務開發中,我們發現,WebSocket推送技術在使用中存在以下問題:首先,WebSocket技術棧不統一,既有基於Netty實現的,也有基於Web容器實現的,給開發和維護帶來困難;其次,WebSocket實現分散在在各個工程中,與業務系統強耦合,如果有其他業務需要整合WebSocket,面臨著重複開發的窘境,浪費成本、效率低下;第三,WebSocket是有狀態協議的,客戶端連線伺服器時只和叢集中一個節點連線,資料傳輸過程中也只與這一節點通訊。
因此,WebSocket叢集需要解決會話共享的問題。如果只採用單節點部署,雖然可以避免這一問題,但無法水平擴充套件支撐更高負載,有單點的風險;最後,缺乏監控與報警,雖然可以透過Linux的Socket連線數大致評估WebSocket長連線數,但數字並不準確,也無法得知使用者數等具有業務含義的指標資料;無法與現有的微服務監控整合,實現統一監控和報警。
2 長連線閘道器的設計與實現為了解決以上問題,我們實現了統一的WebSocket長連線閘道器,具備如下特點:
1. 集中實現長連線管理和推送能力。統一技術棧,將長連線作為基礎能力沉澱,便於功能迭代和升級維護。2. 與業務解耦。將業務邏輯與長連線通訊分離,使業務系統不再關心通訊細節,也避免了重複開發,浪費研發成本。3. 使用簡單。提供HTTP推送通道,方便各種開發語言的接入。業務系統只需要簡單的呼叫,就可以實現資料推送,提升研發效率。4. 分散式架構。實現多節點的叢集,支援水平擴充套件應對業務增長帶來的挑戰;節點宕機不影響服務整體可用性,保證高可靠。5. 多端訊息同步。允許使用者使用多個瀏覽器或標籤頁同時登陸線上,保證訊息同步傳送。6. 多維度監控與報警。自定義監控指標與現有微服務監控系統打通,出現問題時可及時報警,保證服務的穩定性。
2.1. 技術選型
在眾多的WebSocket實現中,從效能、擴充套件性、社群支援等方面考慮,最終選擇了Netty。Netty是一個高效能、事件驅動、非同步非阻塞的網路通訊框架,在許多知名的開源軟體中被廣泛使用。
WebSocket是有狀態的,無法像直接HTTP以叢集方式實現負載均衡,長連線建立後即與服務端某個節點保持著會話,因此叢集下想要得知會話屬於哪個節點,有兩種方案,一種是使用類似微服務的註冊中心來維護全域性的會話對映關係,一種是使用事件廣播由各節點自行判斷是否持有會話,兩種方案對比如表1所示。
方案 |
優點 |
缺點 |
註冊中心 |
會話對映關係清晰,叢集規模較大時更合適 |
實現複雜,強依賴註冊中心,有額外運維成本 |
事件廣播 |
實現簡單更加輕量 |
節點較多時,所有節點均被廣播,資源浪費 |
表1:WebSocket叢集方案
綜合考慮實現成本與叢集規模,選擇了輕量級的事件廣播方案。實現廣播可以選擇基於RocketMQ的訊息廣播、基於Redis的Publish/Subscribe、基於ZooKeeper的通知等方案,其優缺點對比如表2所示。從吞吐量、實時性、持久化、實現難易等方面考慮,最終選擇了RocketMQ。
方案 |
優點 |
缺點 |
基於RocketMQ |
吞吐量高、高可用、保證可靠 |
實時性不如Redis |
基於Redis |
實時性高、實現簡單 |
不保證可靠 |
基於ZooKeeper |
實現簡單 |
寫入效能較差,不適合頻繁寫入場景 |
表2:廣播的實現方案對比
2.2. 系統架構
閘道器的整體架構如圖1所示。
圖1:WebSocket長連線閘道器架構圖1:WebSocket長連線閘道器架構
閘道器的整體流程如下:
1. 客戶端與閘道器任一節點握手建立起長連線,節點將其加入到記憶體維護的長連線佇列。客戶端定時向服務端傳送心跳訊息,如果超過設定的時間仍沒有收到心跳,則認為客戶端與服務端的長連線已斷開,服務端會關閉連線,清理記憶體中的會話。2. 當業務系統需要向客戶端推送資料時,透過閘道器提供的HTTP介面將資料發向閘道器。3. 閘道器在接收到推送請求後,將訊息寫入RocketMQ。4. 閘道器作為消費者,以廣播模式消費訊息,所有節點都會接收到訊息。5. 節點接收到訊息後判斷推送的訊息目標是否在自己記憶體中維護的長連線佇列裡,
如果存在則透過長連線推送資料,否則直接忽略。
閘道器以多節點方式構成叢集,每節點負責一部分長連線,可實現負載均衡,當面對海量連線時,也可以透過增加節點的方式分擔壓力,實現水平擴充套件。同時,當節點出現宕機時,客戶端會嘗試重新與其他節點握手建立長連線,保證服務整體的可用性。
2.3. 會話管理
長連線建立起來後,會話維護在各節點的記憶體中。SessionManager元件負責管理會話,內部使用了雜湊表維護了UID與UserSession的關係;UserSession代表使用者維度的會話,一個使用者可能會同時建立多個長連線,因此UserSession內部同樣使用了一個雜湊表維護Channel與ChannelSession的關係。為了避免使用者無限制的建立長連線,UserSession在內部的ChannelSession超過一定數量後,會將最早建立的ChannelSession關閉,減少伺服器資源佔用。SessionManager、UserSession、ChannelSession的關係如圖2所示。
圖2:SessionManager元件
2.4. 監控與報警為了瞭解叢集建立了多少長連線、包含了多少使用者,閘道器提供了基本的監控與報警能力。閘道器接入了Micrometer,將連線數與使用者數作為自定義指標暴露,供Prometheus進行採集,實現了與現有的微服務監控系統打通。在Grafana中方便地檢視連線數、使用者數、JVM、CPU、記憶體等指標資料,瞭解閘道器當前的服務能力與壓力。報警規則也可以在Grafana中配置,當資料異常時觸發奇信(內部報警平臺)報警。
2.5. 效能壓測
壓測選擇兩臺配置為4核16G的虛擬機器,分別作為伺服器和客戶端。壓測時選擇為閘道器開放了20個埠,同時建立20個客戶端,每個客戶端使用一個服務端埠建立起5萬連線,可以同時建立百萬個連線。連線數與記憶體使用情況如圖3所示。
圖3:百萬級連線
給百萬個長連線同時傳送一條訊息,採用單執行緒傳送,伺服器傳送完成的平均耗時在10s左右,如圖4所示。
圖4:伺服器推送耗時
一般同一使用者同時建立的長連線都在個位數。以10個長連線為例,在併發數600、持續時間120s條件下壓測,推送介面的TPS大約在1600+,如圖5所示。
圖5 :長連線10、併發600、持續時間120s的壓測資料
當前的效能指標已滿足愛奇藝號的實際業務場景,可支援未來的業務增長。
3 業務案例為了更生動的說明最佳化效果,文章最後,我們也以封面圖新增濾鏡效果為例,介紹一個愛奇藝號使用WebSocket閘道器的案例。
愛奇藝號自媒體發表影片時,可選擇為封面圖新增濾鏡效果,引導使用者提供提供更優質的封面。當用戶選擇一個封面圖後,會提交非同步的後臺處理任務。當非同步任務處理完成後,透過WebSocket將不同濾鏡效果處理後的圖片返回給瀏覽器,業務場景如圖6所示。
圖6:愛奇藝號影片封面圖濾鏡
從研發效率方面考慮,如果在業務系統中整合WebSocket,至少需要1-2天的開發時間;如果直接使用閘道器的推送能力,只需要簡單的介面呼叫就實現了資料推送,開發時間降低到分鐘級別,研發效率大大提高。從運維成本方面考慮,業務系統不再含有與業務邏輯無關的通訊細節,程式碼的可維護性更強,系統架構變得更加簡單,運維成本大大降低。
4 寫在最後WebSocket是目前實現服務端推送的主流技術,恰當使用能夠有效提供系統響應能力,提升使用者體驗。透過WebSocket長連線閘道器可以快速為系統增加資料推送能力,有效減少運維成本,提高開發效率。
長連線閘道器的價值在於它封裝了WebSocket通訊細節,與業務系統解耦,使得長連線閘道器與業務系統可獨立最佳化迭代,避免重複開發,便於開發與維護。其次,閘道器提供了簡單易用的HTTP推送通道,支援多種開發語言接入,便於系統整合和使用。另外,閘道器採用了分散式架構,可以實現服務的水平擴容、負載均衡與高可用。最後,閘道器集成了監控與報警,當系統異常時能及時預警,保證服務的健康和穩定。
目前,WebSocket長連線閘道器已在愛奇藝號圖片濾鏡結果通知、MCN電子簽章等多個業務場景中得到應用。未來還有許多方面需要探索,例如訊息的重發與ACK、WebSocket二進位制資料的支援、多租戶的支援等。我們也會不斷最佳化、豐富功能,帶給開發者更好的使用體驗。