100 萬級連接,愛奇藝 WebSocket 網關如何架構

原文作者:愛奇藝技術團隊

HTTP 協議屬於一種無狀態、基於 TCP 的請求 / 響應模式的協議,

HTTP 協議中,只有客戶端能發起請求,由服務端進行迴應。

雖然,在許多情況下,這種請求 / 響應的拉取模式能夠滿足需求。

然而,在特定情況下,例如實時通知(如 IM 中的離線消息推送最爲典型)和消息推送等應用場景,需要將數據實時推到客戶端,這就要求服務端具備主動推送數據的能力。

如何推呢?

傳統的 Web 服務端推送技術,包括短輪詢、長輪詢等,雖然能在一定程度上解決問題,但也存在如時效性、資源浪費等問題。

HTML5 標準推出的 WebSocket 規範基本改變了這種狀況,已經成爲當前服務端消息推送技術的主流。

本文將分享愛奇藝在基於 Netty 實現 WebSocket 長連接實時推送網關過程中的實踐經驗和總結。

1、舊方案存在的技術痛點

愛奇藝號作爲我們內容生態的關鍵部分,作爲前端系統,對用戶體驗有着較高的要求,這直接影響着創作者的創作熱情。

當前,愛奇藝號在多個業務場景中應用了 WebSocket 實時推送技術,包括

1)用戶評論:實時地將評論消息推送至瀏覽器;

2)實名認證:在合同簽署前,需要對用戶進行實名認證,用戶掃描二維碼後進入第三方的認證頁面,認證完成後異步通知瀏覽器認證狀態;

3)活體識別:類似於實名認證,當活體識別完成後,異步將結果通知瀏覽器。

在實際業務開發中,我們發現 WebSocket 實時推送技術在使用過程中存在一些問題。

這些問題是

1)首先:WebSocket 技術棧不統一,既有基於 Netty 實現的,也有基於 Web 容器實現的,給開發和維護帶來困難;

2)其次:WebSocket 實現分散在各個工程中,與業務系統緊密耦合,如果有其他業務需要集成 WebSocket,將面臨重複開發的困境,浪費成本、效率低下;

3)第三:WebSocket 是有狀態協議,客戶端連接服務器時只與集羣中一個節點連接,數據傳輸過程中也只與這一節點通信。WebSocket 集羣需要解決會話共享的問題。如果只採用單節點部署,雖然可以避免這一問題,但無法水平擴展以支持更高的負載,存在單點故障風險;

4)最後:最後:缺乏監控與報警,雖然可以通過 Linux 的 Socket 連接數大致評估 WebSocket 長連接數,但數字並不準確,也無法得知用戶數等具有業務含義的指標數據;無法與現有的微服務監控整合,實現統一監控和報警。

2、新方案的技術目標

如上所述,爲了解決舊方案中存在的問題,我們需要實現統一的 WebSocket 長連接實時推送網關。

這套新的網關需要具備以下特點

1)集中實現長連接管理和推送能力:採用統一的技術棧,將長連接作爲基礎功能進行沉澱,以便於功能的迭代和維護;

2)與業務解耦:將業務邏輯與長連接通信分離,使得業務系統無需關心通信細節,避免了重複開發,節約了研發成本;

3)使用簡單:提供 HTTP 推送通道,便於各種開發語言的接入。業務系統只需進行簡單的調用,便可實現數據推送,從而提高研發效率;

4)分佈式架構:構建多節點的集羣,支持水平擴展以應對業務增長帶來的挑戰;節點故障不會影響服務的整體可用性,確保高可靠性;

5)多端消息同步:允許用戶使用多個瀏覽器或標籤頁同時登錄在線,確保消息同步發送;

6)多維度監控與報警:將自定義監控指標與現有的微服務監控系統連接,當出現問題時可以及時報警,保證服務的穩定性。

3、新方案的技術選型

在衆多的 WebSocket 實現中,經過對性能、擴展性、社區支持等各方面的權衡,我們最終確定了 Netty。

Netty 是一個高性能、事件驅動、異步非阻塞的網絡通信框架,已在許多知名的開源項目中得到廣泛應用。

WebSocket 具有狀態特性,這與 HTTP 的無狀態特性不同,因此無法像 HTTP 一樣通過集羣方式實現負載均衡。在長連接建立後,它會與服務端的某個節點保持會話,所以在集羣環境下,要確定會話屬於哪個節點會有些困難。

解決以上問題一般有兩種技術方案

1)一種是使用類似於微服務註冊中心的技術來維護全局的會話映射關係;

2)另一種是使用事件廣播,由各節點自行判斷是否持有會話。這兩種方案的對比如下表所示。

WebSocket 集羣方案

AXyERE

考慮到實現成本和集羣規模,我們選擇了輕量級的事件廣播方案。

實現廣播的方法有多種,如基於 RocketMQ 的消息廣播、基於 Redis 的 Publish/Subscribe、基於 ZooKeeper 的通知等。

這些方案的優缺點對比如下表所示。在考慮到吞吐量、實時性、持久化和實現難易程度等因素後,我們最終選擇了 RocketMQ。

廣播的實現方案對比

i7GsT4

4、新方案的實現思路

4.1 系統架構

網關的整體架構如下圖所示

網關的整體流程如下

1) 客戶端與網關的任何一個節點建立長連接,節點會將其加入到內存中的長連接隊列。客戶端會定期向服務端發送心跳消息,若超過設定時間還未收到心跳,則認爲客戶端與服務端的長連接已斷開,服務端會關閉連接,清理內存中的會話。

2) 當業務系統需要向客戶端推送數據時,通過網關提供的 HTTP 接口 將數據發送至網關。

3) 在收到推送請求後,網關會將消息 寫入 RocketMQ

4) 網關作爲消費者,以 廣播模式 消費消息,所有節點都能收到消息。

5) 節點在收到消息後會判斷推送的消息目標是否在其內存中維護的長連接隊列裏,如果 存在則通過長連接推送數據,否則直接忽略。

網關通過多節點構成集羣,每個節點負責一部分長連接,實現負載均衡。當面臨大量連接時,也可以通過增加節點來分散壓力,實現水平擴展。

同時,當節點出現故障時,客戶端會嘗試與其他節點重新建立長連接,確保服務的整體可用性。

4.2 會話管理

在 WebSocket 長連接建立後,會話信息會保存在各個節點的內存中。

SessionManager 組件負責管理會話,它內部使用哈希表來維護 UID 與 UserSession 的關聯。

UserSession 表示用戶層面的會話,一個用戶可能同時擁有多個長連接,因此 UserSession 內部同樣使用哈希表來維護 Channel 與 ChannelSession 的關聯。

爲了防止用戶無休止地創建長連接,當 UserSession 內部的 ChannelSession 超過一定數量時,它會關閉最早建立的 ChannelSession,以減少服務器資源的佔用。

SessionManager、UserSession、ChannelSession 的關係如下圖所示。

SessionManager 組件

注意:請點擊圖像以查看清晰的視圖!

4.3 監控與報警

爲了掌握集羣中建立的長連接數量和包含的用戶數量,網關提供了基本的監控和報警功能。

網關接入了 Micrometer (https://www.oschina.net/p/micrometer?hmsr=aladdin1e1),將連接數和用戶數作爲自定義指標暴露,供 Prometheus (https://prometheus.io/) 進行採集,從而實現了與現有的微服務監控系統打通。

Grafana (https://grafana.com/) 中,可以方便地查看連接數、用戶數、JVM、CPU、內存等指標數據,瞭解網關當前的服務能力和壓力。報警規則也可以在 Grafana 中配置,當數據異常時觸發奇信(內部報警平臺)報警。

5、新方案的性能壓測

壓測準備

連接數(百萬級)與內存使用情況如下圖所示

[root@sy-dev-1de4f0c2a target]# ss -s ; free -h
Total: 1002168 (kernel 1002250)
TCP: 1002047 (estab 1002015, closed 4, orphaned 0, synrecv 0, timewait 4/0), ports 0
Transport Total   IP      IPv6
*         1002250 -       -
RAW       0       0       0
UDP       4       2       2
TCP       1002043 1002041 2
INET      1002047 1002043 4
FRAG      0       0       0

          total   used    free  shared  buff/cache  available
Mem:      15G     4.5G    4.5G  232K    6.5G        8.2G
Swap:     4.0G    14M     4.0G

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

服務器推送耗時

2021-01-25 20:51:02.614 INFO [mp-tcp-gateway,54d52e7e4240b65a,54d52e7e4240b65a,false]
[600ebeb62@2559f4507adee3b316c571/507adee3b316c571] 89558 --- [nio-8080-exec-6]
c.i.m.t.g.controller.NotifyController: [] [UID:] send message ...
2021-01-25 20:51:11.973 INF0 [mp-tcp-gateway,54d52e7e4240b65a,54d52e7e4240b65a,false]
[1600ebeb62@2559f4507adee3b316c571/507adee3b316c571] 89558 --- [nio-8080-exec-6]
c.i.m.t.g.controller.NotifyController: [] [UID:] send message to 1001174 channels

一般同一用戶同時建立的長連接都在個位數。

以 10 個長連接爲例,在併發數 600、持續時間 120s 條件下壓測,推送接口的 TPS 大約在 1600+,如下圖所示。

長連接 10、併發 600、持續時間 120s 的壓測數據

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

6、新方案的實際應用案例

爲了更形象地展示優化效果,文章最後,我們以封面圖添加濾鏡效果爲例,介紹了一個愛奇藝號採用新 WebSocket 網關方案的實例。

愛奇藝號自媒體在發佈視頻時,可以選擇爲封面圖添加濾鏡效果,引導用戶提供更高質量的封面。

當用戶選擇封面圖後,會提交一個異步的後臺處理任務。

一旦異步任務完成,通過 WebSocket 將不同濾鏡效果處理後的圖片返回給瀏覽器,業務場景如下圖所示。

從研發效率的角度來看,如果在業務系統中集成 WebSocket,至少需要 1-2 天的開發時間。

而直接使用新的 WebSocket 網關的推送功能,只需簡單的接口調用就能實現數據推送,將開發時間降低到分鐘級別,大幅提高研發效率。

從運維成本的角度來看,業務系統不再包含與業務邏輯無關的通信細節,代碼的可維護性更強,系統架構變得更簡單,運維成本大幅降低。

7、總結

WebSocket 是實現服務端推送的主流技術,適當使用可以有效提升系統響應能力,增強用戶體驗。

通過 WebSocket 長連接網關,可以迅速爲系統增加數據推送能力,有效降低運維成本,提高開發效率。

長連接網關的價值在於

目前,新的 WebSocket 長連接實時網關已在愛奇藝號圖片濾鏡結果通知、MCN 電子簽章等多個業務場景中得到應用。

未來還有許多方面需要探索,例如消息的重發與 ACK、WebSocket 二進制數據的支持、多租戶的支持等。

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