本文由嗶哩嗶哩資深開發工程師黃山成分享,原題“千萬長連消息系統”,本文進行了排版和內容優化等。
1、引言
在當今數字娛樂時代,彈幕已經成為直播平臺上不可或缺的互動元素之一。
用戶通過發送彈幕、送禮等,可以實時在直播畫面上展現自己的想法、評論和互動內容,從而豐富了用戶觀看體驗。在這個過程中,實時向終端推送互動信息,就需要用到長連接。
長連接,顧名思義,是應用存活期間和服務端一直保持的網絡數據通道,能夠支持全雙工上下行數據傳輸。其和請求響應模式的短連接服務最大的差異,在于它可以提供服務端主動給用戶實時推送數據的能力。
本文將介紹B站基于golang實現的千萬級長連接實時消息系統的架構設計與實踐,包括長連接服務的框架設計,以及針對穩定性與高吞吐做的相關優化。
2、關聯文章
3、架構設計
3.1概述
長連接服務是多業務方共同使用一條長連接。
因為在設計時,需要考慮到不同業務方、不同業務場景對長連接服務的訴求,同時也要考慮長連接服務的邊界,避免介入業務邏輯,影響后續長連接服務的迭代和發展。
長連接服務主要分為三個方面:
- 1)長連接建立、維護、管理;
- 2)下行數據推送;
- 3)上行數據轉發(目前只有心跳,還沒實際業務場景需求)。
3.2整體架構
長連接服務整體構架如上圖所示,整體服務包含以下幾個部分。
1)控制層:建連的前置調用,主要做接入合法性校驗、身份校驗和路由管控。
主要職責:
- 1)用戶身份鑒權;
- 2)加密組裝數據,生成合法token;
- 3)動態調度分配接入節點。
2)接入層:長連接核心服務,主要做卸載證書、協議對接和長連接維護。
主要職責:
- 1)卸載證書和協議;
- 2)負責和客戶端建立并維護連接,管理連接id和roomid的映射關系;
- 3)處理上下行消息。
3)邏輯層:簡化接入層,主要做長連的業務功能。
主要職責:
- 1)在線人數上報記錄;
- 2)記錄連接ID各屬性和各節點的映射關系。
- 4)消息分發層:消息推送到接入層。
主要職責:
5)服務層:業務服務對接層,提供下行消息推送入口。
主要職責:
- 1)管控業務推送權限;
- 2)消息檢測和重組裝;
- 3)消息按一定策略限流,保護自身系統。
3.3核心流程
長連接主要是3個核心流程:
- 1)建立連接:由客戶端發起,先通過控制層,獲取該設備合法的token和接入點配置;
- 2)維持連接:主要是客戶端定時發起心跳,來保證長連接活躍;
- 3)下行推送:下行推送由業務Server發起,經由服務層根據相關標識確定連接標識和接入節點,經過消息分發層,把推送到對應的接入層,寫入到指定連接上,然后下發到客戶端。
3.4功能列表
結合B站業務場景,下行數據推送,提供如下通用功能:
- 1)用戶級消息:指定推送給某些用戶(比如給某個主播發送邀請pk消息);
- 2)設備級消息:制定推送給某些設備(比如針對未登陸的設備,推送客戶端日志上報指令);
- 3)房間級消息:給某房間內的連接推送消息(比如給直播間的所有在線用戶推送彈幕消息);
- 4)分區消息:給某分區的房間推送消息(比如給某個分區下,所有開播的房間,推送某個營收活動);
- 5)全區消息:給全平臺用戶推送消息(比如給全部在線用戶推送活動通知)。
4、高吞吐技術設計
隨著業務發展壯大,在線用戶越來越多,長連系統的壓力越來越大,尤其是熱門賽事直播,比如s賽期間,全平臺在線人數快達到千萬,消息吞吐量有上億,長連系統消息分發平均延遲耗時在1s左右,消息到達率達到99%,下面具體分析下長連做了哪些措施。
4.1網絡協議
選擇合適的網絡協議對于長連接系統的性能至關重要:
- 1)TCP協議:可以提供可靠的連接和數據傳輸,適用于對數據可靠性要求較高的場景;
- 2)UDP協議:是一個不可靠的協議,但是傳輸效率高,適用于對數據可靠性要求不高的場景;
- 3)WebSocket協議:也是實現雙向通信而不增加太多的開銷,更多的用于web端。
接入層拆分成協議模塊和連接模塊:
- 1)協議模塊:和具體的通訊層協議交互,封裝不同通訊協議的接口和邏輯差異。
- 2)連接模塊:維護長連接業務連接狀態,支持請求上行、下行等業務邏輯,維護連接各屬性,以及和房間id的綁定關系。
針對以上第 1)點,協議模塊同時給連接模塊提供統一的數據接口,包括連接建立、數據讀取、寫入等。后續增加新協議,只要在協議模塊做適配,不影響其他模塊的長連業務邏輯。
優勢在于:
- 1)業務邏輯和通訊協議做了隔離,方便迭代增加通訊協議,簡化兼容多通訊協議的實現難度;
- 2)控制層可以根據客戶端的實際情況,下發更優的通訊協議。
4.2負載均衡
采用負載均衡技術可以將請求分發到不同的服務器節點上處理,避免了單一節點的負載過高,提高了系統的擴展性和穩定性。
長連增加控制層,做負載均衡。控制層提供http短連接口,基于客戶端和各邊緣節點實際情況,根據就近原則,動態選擇合適的接入節點。
接入層支持水平擴展,控制層可以實時增加、減少分配節點。在S賽期間,在線人數快到達千萬時,平衡調度各接入節點,保障了各節點的CPU和內存都在穩定的范圍內。
4.3消息隊列
消息推送鏈路是:業務發送推送,經過服務層推到邊緣節點,然后下發給客戶端。
服務層實時分發到各邊緣節點,如果是房間類型消息,需要推到多個邊緣節點,服務層同時還要處理業務邏輯,很影響消息的吞吐量。
所以增加消息隊列和消息分發層,消息分發層維護各邊緣節點信息和推送消息,提高了系統的并發處理能力和穩定性,避免了因消息推送阻塞而導致的性能問題。
4.4消息聚合
當有熱門賽事時,同時在線可能達到千萬級別,一條彈幕消息就要擴散到千萬個終端,假如在線的每個人每秒發一條,需要發送消息量就是1kw*1kw,消息量非常大,此時消息分發層和接入層,壓力都會很大。
分析發現:這些消息都是同一個房間的,屬于熱點房間,比如s賽房間,觀眾數量是無法減少的,那只能在消息數上做文章。業務消息推送不能減少,又要減少擴散的消息數,就想到了消息聚合。
針對房間消息,按照一定的規則進行消息聚合,批量推送:
消息聚合上線后,消息分發層對接入層調用QPS下降60%左右,極大的降低了接入層和消息分發層的壓力。
4.5壓縮算法
消息聚合后,降低了消息的數量,但是增加了消息體的大小,影響了寫入IO,需要減少消息體大小,就想到了消息壓縮。
壓縮算法,選了市面上比較常用的兩個:zlib和brotli,進行比較。
抓取了線上業務推送的數據,選擇最高等級的壓縮等級,進過壓縮驗證:
由此可見,brotli相比zlib有很大的優勢,最后選擇了brotli壓縮算法。
選擇在消息分發層進行消息壓縮,避免在各接入節點多次重復壓縮,浪費性能。上線后提升吞吐量的同時,也降低的寬帶使用成本。
5、服務保障技術設計
現在有些業務是強依賴長連推送消息,消息丟失,輕則影響用戶體驗,重則阻塞業務后續流程,進而影響業務流水。針對長連服務消息保障,做了如下工作。
5.1多活部署
多活部署,通過在不同地理位置部署相同的系統架構和服務,實現了系統在單一地域故障時的快速故障轉移,從而提高了系統的穩定性和可用性。
長連服務部署,主要做了以下幾點:
- 1)長連接在國內華東、華南、華北地域均部署了接入點,支持三大運營商;華南和華中自建機房也部署了接入點;為支持海外用戶,增加了新加坡機房獨立接入點;
- 2)針對業務場景不同,在云上節點和自建節點之間,實時切換,因為云上節點和自建機房的成本是不一樣的,在保證服務質量的前提下,盡可能的控制成本。
目前線上運行過程中,偶爾會遇到單節點或機房的網絡抖動,通過控制層,對有問題的節點,進行秒級摘流,大大減少了對業務的影響。
5.2高低消息通道
多業務消息接入長連接,但不同消息之間的重要性是不一樣的,比如彈幕消息和邀請pk消息,丟失幾條彈幕對用戶體驗不會影響很大,但如果邀請pk消息丟失,則會導致pk業務無法進行后續的流程。
針對不同等級的消息,采用了高低優消息通道。重要消息走高優通道,普通消息走低優通道。這樣重要和普通消息進行了物理隔離,消息分發優先保證重要消息。
針對高優通道,做了雙投遞的保障,在接入層做冪等去重。首先重要消息是針對用戶級別的,量不會很大,所以對接入層的壓力不會增加很大。另外雙投遞的job是部署在多機房的,這也就降低單機房網絡抖動造成的影響。
高低優通道上線后,遇到過內網出網抖動,當時內網部屬的job節點推送消息異常,而云上高優job節點可正常推送,很好的保障了高優消息的到達,進而保障了高優業務不受影響。
5.3高達功能
高低優通道解決的是job到接入層的這一個環節,但消息推送聯路涉及到多個環節,比如服務層到job、接入層到客戶端。
針對整個鏈路,通過實現必達機制來確保終端的到達率,簡稱高達功能。
功能實現:
- 1)每條消息引入msgID,客戶端收到消息后進行冪等去重和ack回執;
- 2)服務端針對msgid進行ack檢測,針對未ack的,有效期內再次重試下發。
最終到達率 = (1-(1-r)^(n+1)),其中:r為廣播單次到達率,n為最大重試次數。
例如:r = 97%、n=2,那么最終到達率可以達到(1-(1-0.97)^(2+1)) = 99.9973%
6、進出”房“消息的送達保證設計
有些業務場景,需要用到用戶進出房消息,比如用戶A進入直播間,頁面會顯示歡迎用戶A進入房間,或者是加入在線榜單。
1)進房消息會存在丟失,需要有補償機制。想到可以通過連接心跳來補償進房消息,但心跳是持續不斷的,連接在線期間,業務希望只收到一次進房消息,所以進房消息需要有冪等機制。
2)出房消息也會存在丟失,如果丟失了,業務無法從在線榜單剔除用戶,此時也需要有補償機制。此時就需要增加連接的狀態機,通過心跳維護狀態機,當心跳丟失時,認為連接斷開,用戶退房。
7、未來規劃
統一長連接服務經歷數次迭代后,目前基本功能已經趨于穩定,后續對長連接服務進行改善和優化。
主要集中在以下幾個方向:
- 1)數據化:進一步完善長連接全鏈路網絡質量數據統計和高價值消息全鏈路追蹤的能力;
- 2)智能化:端上建聯、接入點選擇等能夠根據實際環境進行自動化調整;
- 3)性能優化:接入層的連接模塊中,處理上下行消息的攜程進行共享,減少接入層的攜程數,進一步提升單機性能和連接數;
- 4)功能擴展:新增離線消息功能等。
8、參考資料
[1] 手把手教你寫基于TCP的Socket長連接
[2] 正確理解IM長連接、心跳及重連機制,并動手實現
[3] 萬字長文:手把手教你實現一套高效的IM長連接自適應心跳保活機制
[4] 用JWT技術解決IM系統Socket長連接的身份認證痛點
[5] TCP/IP詳解 - 第11章·UDP:用戶數據報協議
[6] TCP/IP詳解 - 第17章·TCP:傳輸控制協議
[7] WebSocket從入門到精通,半小時就夠!
[8] 快速理解TCP協議一篇就夠
[9] 快速理解TCP和UDP的差異
[10] 一泡尿的時間,快速搞懂TCP和UDP的區別
[11] 到底什么是Socket?一文即懂!
[12] 我們在讀寫Socket時,究竟在讀寫什么?
[13] 假如你來設計TCP協議,會怎么做?
[14] 深入操作系統,一文搞懂Socket到底是什么
[15] 通俗易懂,高性能服務器到底是如何實現的
[16] 12306搶票帶來的啟示:看我如何用Go實現百萬QPS的秒殺系統(含源碼)
(本文已同步發布于:http://www.52im.net/thread-4647-1-1.html)