百度直播消息服務(wù)架構(gòu)實(shí)踐
直播間內(nèi)用戶聊天互動,形式上是常見的IM消息流;但直播消息流不僅僅是用戶聊天。除用戶聊天外,直播間內(nèi)常見的用戶送禮物、進(jìn)場、點(diǎn)贊、去購買、主播推薦商品、申請連麥等互動行為的實(shí)時(shí)提醒,也是通過消息流下發(fā)的。此外,直播間關(guān)閉、直播流切換等特殊場景,也依賴消息流的實(shí)時(shí)下發(fā)。消息流可以認(rèn)為是直播間內(nèi)主播與用戶間實(shí)時(shí)互動和直播間實(shí)時(shí)控制的基礎(chǔ)能力。
如何構(gòu)建直播的消息系統(tǒng),又有哪些挑戰(zhàn)需要解決,我們來梳理一下。
2.1 直播消息場景分析
直播間內(nèi)聊天消息,經(jīng)常被類比于群聊。群聊是大家比較熟悉的即時(shí)通訊場景,直播間內(nèi)聊天和群聊,二者有相似性,但也有本質(zhì)的區(qū)別。
對比二者的特點(diǎn):
- 同時(shí)參與人數(shù)不同: 群聊的參與人數(shù)上千人就是很大的群了;但對于高熱度的大型直播場景,例如國慶、閱兵、春晚等,單直播間累計(jì)用戶是百萬甚至千萬量級的集合,同時(shí)在線人數(shù)可達(dá)數(shù)百萬人。
- 用戶與群和直播間的關(guān)系不同: 用戶進(jìn)群退群,是相對低頻的操作,用戶集合相對固定,用戶進(jìn)出的變更頻度不會特別高;而用戶進(jìn)出直播間,是非常頻繁的,高熱度直播的單直播間每秒面臨上萬用戶的進(jìn)出變更。
- 持續(xù)時(shí)間不同: 群聊建立后,聊天持續(xù)時(shí)間可能比較長,幾天到數(shù)月都有;而直播間大部分持續(xù)不超過幾個(gè)小時(shí)。
問題一:直播間內(nèi)用戶的維護(hù)
- 單直播間每秒上萬用戶的進(jìn)出變更;實(shí)際進(jìn)入直播間峰值不超過2萬QPS,退出也不超過2萬QPS。
- 單直播間同時(shí)數(shù)百萬用戶在線
- 單直播間累計(jì)用戶達(dá)千萬量級
支持在線百萬、累積千萬兩個(gè)集合,每秒4萬QPS更新,有一定壓力,但有支持高讀寫性能的存儲應(yīng)該可以解決,例如redis。 問題二:百萬在線用戶的消息下發(fā)
面對百萬在線用戶,上下行都有大量的消息,從直播用戶端視角分析:
- 實(shí)時(shí)性: 如果消息服務(wù)端做簡單消峰處理,峰值消息的堆積,會造成整體消息延時(shí)增大,且延時(shí)可能產(chǎn)生很大的累積效應(yīng),消息與直播視頻流在時(shí)間線上產(chǎn)生很大的偏差,影響用戶觀看直播時(shí)互動的實(shí)時(shí)性。
- 端體驗(yàn)和性能: 端展示各類用戶聊天和系統(tǒng)消息,一般一屏不超過10-20條;如果每秒有超過20條的消息下發(fā),端上展示的消息基本會持續(xù)刷屏;再考慮到有禮物消息的特效等;大量的消息,對端的處理和展示,帶來持續(xù)高負(fù)荷。所以,對于一個(gè)長時(shí)間觀看直播的用戶端來說,如果出現(xiàn)持續(xù)的大量消息,端的消息消費(fèi)會有顯著的性能壓力,且過多消息會有累積效應(yīng)。
由于問題一不難解決,以下主要討論問題二。
2.2 直播消息設(shè)計(jì)目標(biāo)
綜合考慮直播業(yè)務(wù)場景,對于消息服務(wù)的需求目標(biāo)如下:
- 實(shí)時(shí)性方面,端和端的消息要達(dá)到秒級;
- 性能方面,消息服務(wù)能支持同一直播間內(nèi)百萬以上用戶同時(shí)在線下發(fā);
- 而對于峰值的過多消息,丟棄是合理適當(dāng)?shù)奶幚矸绞剑?/span>
- 基于合理的端用戶體驗(yàn),單直播間內(nèi)每秒消息數(shù)假設(shè)不超過N條。
現(xiàn)在,問題的核心是,如何做到把不超過N條的消息,在S秒內(nèi),下發(fā)到直播間內(nèi)的百萬用戶,假設(shè)N<=20,S<=2。
2.3 普通群聊壓力分析
2.3.1 普通群聊消息收發(fā)分析

首先,具體分析一下普通群聊的消息收發(fā)流程:
- 對于群group-1,分配一個(gè)群公共消息信箱group-mbox-1;
- 群group-1內(nèi)的用戶user-1,由手機(jī)端APP-1上發(fā)出消息msg-1;
- 服務(wù)端接收到消息msg-1,檢查user-1是否有權(quán)限,如有權(quán)限,將msg-1存儲到群信箱group-mbox-1,生成相應(yīng)msgID-1;
- 服務(wù)端查詢group-1對應(yīng)的用戶列表groupUserList-1;
- 基于groupUserList-1拆分出所有獨(dú)立群用戶:user-1、user-2。。。user-n;
- 對于每一個(gè)用戶user-i來說,需要查詢用戶user-i的所在設(shè)備device-i-1、device-i-2、device-i-m(因?yàn)橐粋€(gè)賬號可能登錄多個(gè)設(shè)備);
- 對于每個(gè)設(shè)備device-i-j來說,長連接通道都會建立一個(gè)獨(dú)立的長連接connect-j以服務(wù)于該設(shè)備;但由于connect-j是由端上APP-1連接到長連接服務(wù)的,具有動態(tài)性,所以,查詢device-i-j與connect-j的對應(yīng)關(guān)系時(shí),需要依賴一個(gè)路由服務(wù)route來完成查詢;
- 在查得connect-j后,可以通過connect-j下發(fā)msg-1的通知groupmsg-notify-1;
- 如果用戶user-i正在使用device-i-j的手機(jī)端APP-1,用戶user-i就可以立即從長連接connect-j上收到msg-1的通知groupmsg-notify-1;
- 在接收到groupmsg-notify-1后,手機(jī)端APP-1中的消息SDK根據(jù)端本地歷史消息記錄的最后一條消息latestMsg對應(yīng)的消息ID即latestMsgID,來向服務(wù)端發(fā)起拉消息請求fetchMsg,拉取group-1中從latestMsgID+1到最新的所有消息;
- 服務(wù)端收到拉消息請求fetchMsg后,從group-mbox-1中取出latestMsgID+1到最新的所有消息,返回給端;如果消息過多,可能需要端分頁拉?。?/span>
- 端APP-1拉取到group-1中從latestMsgID+1到最新的所有消息,可以做展示;在用戶在會話中閱讀后,需要設(shè)置所有新消息的已讀狀態(tài)或者會話已讀狀態(tài)。
2.3.2 普通群聊主要壓力
如果完全重用普通群聊消息的下發(fā)通知到端拉取的全過程,對于user-1發(fā)的一條消息msg-1,如果需要支持一個(gè)實(shí)時(shí)百萬量級的群消息,大概有以下幾個(gè)每秒百萬量級的挑戰(zhàn):
首先,秒級拆分出用戶列表groupUserList-1,需要秒級讀出百萬的用戶列表數(shù)據(jù),對于存儲和服務(wù)是第一個(gè)百萬級挑戰(zhàn)。
第二,對于拆分出群中的所有獨(dú)立用戶user-i,需要秒級查詢出百萬量級的device-i-j,對于存儲和服務(wù)是第二個(gè)百萬級挑戰(zhàn)。
第三,對于所有device-i-j,通過動態(tài)路由服務(wù)route,需要秒級查詢出百萬量級的connect-j,對于存儲和服務(wù)是第三個(gè)百萬級挑戰(zhàn)。
第四,對于通過長連接connect-j下發(fā)時(shí),需要支持秒級下發(fā)百萬量級的群消息通知groupmsg-notify-1到對應(yīng)的connect-j上,對于長連接服務(wù)是個(gè)百萬級的挑戰(zhàn)。
第五,對于收到消息通知的所有端APP-1,需要支持百萬QPS端從服務(wù)端拉取消息請求fetchMsg,對于消息信箱服務(wù),這是也是一個(gè)百萬量級的挑戰(zhàn);考慮到實(shí)際各端latestMsgID可能不同,可能的優(yōu)化方式會更復(fù)雜一些,帶來的性能影響會更大。
第六,如果在絕大多數(shù)用戶是在線聊天的場景,設(shè)置已讀狀態(tài)也會有百萬量級QPS對服務(wù)端的壓力。
顯然,完全重用群聊的消息流程,對消息服務(wù)和長連接服務(wù)帶來的壓力是巨大的。
2.3.3 普通群聊優(yōu)化方案
現(xiàn)在,我們來分析以上每個(gè)百萬量級的挑戰(zhàn),是否有優(yōu)化的空間。
-
對于①拆分用戶列表和②查詢用戶對應(yīng)設(shè)備,如果存儲上將二者合并集中起來,也就是優(yōu)化直播間內(nèi)用戶列表的存儲,擴(kuò)展設(shè)備信息,可以減少一次user->device的百萬QPS查詢,可以優(yōu)化。
-
對于④下行通知和⑤端拉取fetchMsg的可靠消息拉取模式,考慮到直播消息允許部分折損丟棄,可以只做單向消息下發(fā),而不做拉取,對于大部分連接保持在線的用戶,也是可以接受的。所以可以優(yōu)化,只保留下行通知(包含消息體),而舍棄端拉取。
-
對于⑥消息設(shè)置已讀,直播場景下可以考慮簡化舍棄。
對于①拆分用戶列表,支持百萬量級用戶列表查詢,比較常規(guī)的思路是支持基于群groupID的批量查詢,例如一次可以查出100個(gè)用戶,1萬QPS查詢就可以支持到百萬;基于群groupID把用戶數(shù)據(jù)的存儲,分散到多個(gè)主從實(shí)例和分片上,控制好打散粒度不出現(xiàn)熱點(diǎn),基本能做到,只是存儲資源可能消耗較多。
對于③動態(tài)路由查詢,表面上看,面臨的問題與①類似,但卻有些不同。因?yàn)槿旱挠脩袅斜?,是基于群groupID做key,建立一個(gè)表或多個(gè)打散的表;而device-i-j的查詢是完全分散的,也是需要批量查詢能力,但是完全分散的設(shè)備信息查詢,不能只針對特定key做優(yōu)化,需要?jiǎng)討B(tài)路由服務(wù)支持整體上達(dá)到百萬QPS的查詢性能。
對于④長連接服務(wù)下發(fā),由于長連接服務(wù)不依賴外部的存儲服務(wù),如果整體要支持百萬量級的下發(fā)能力,若長連接單實(shí)例能支持1萬的下發(fā)能力,整體上100個(gè)實(shí)例就能支持到百萬量級下發(fā)。
基于以上分析,支持百萬量級的消息下發(fā),初見曙光。似乎只要優(yōu)化好用戶列表、動態(tài)路由的存儲/查詢和長連接的容量擴(kuò)容,但所有的前提是需要消耗大量存儲和機(jī)器資源。 考慮到直播業(yè)務(wù)的實(shí)際情況,現(xiàn)實(shí)不容樂觀:
一方面,平時(shí)沒有熱點(diǎn)直播時(shí),可能單場直播并發(fā)在線用戶數(shù)峰值不超過1萬人,甚至不到1000;在業(yè)務(wù)初期,整體直播在線用戶峰值可能也不超過10萬。這就意味著,為了支持百萬量級的峰值,資源整體上有幾十倍的冗余。
另一方面,如果突然來了一場熱度非常高的直播,可能需要支持的不只是100萬量級消息下發(fā),可能是500萬以上的量級(例如國慶閱兵、春晚等)。這樣的話,每次大型直播得提前預(yù)估可能的在線用戶峰值,如果超過當(dāng)前設(shè)計(jì)容量,需要對①用戶列表③動態(tài)路由查詢④長連接服務(wù),分別擴(kuò)容和壓測;或者在可接受的情況下,做服務(wù)降級或拒絕服務(wù)。
而實(shí)際上,在線用戶峰值量級很難估計(jì)準(zhǔn)確,這樣會造成實(shí)際資源利用率很低,擴(kuò)縮容的操作頻繁,運(yùn)維成本高。是否選擇這個(gè)方案,也是很令人糾結(jié)。
2.3.4 普通群聊多群組方案
也有人提過拆分多個(gè)群組的方案,例如,如果一個(gè)群組最多支持1萬用戶,開100個(gè)群就可以支持一百萬用戶;再建立一個(gè)虛擬群,將這100個(gè)群關(guān)聯(lián)起來,似乎可行。
但如果仔細(xì)分析,會發(fā)現(xiàn)以上提到的幾個(gè)問題①拆分用戶列表③動態(tài)路由查詢④長連接下發(fā),高壓力依然存在,還是不可避免。
除此之外,多群組還會引入其他問題:
問題一: 多群組消息不同步。如果兩個(gè)用戶在一起看直播,而所屬群不同,看到的消息會完全不同。
問題二: 直播場景用戶是動態(tài)進(jìn)出的,也就是說群組成員非常不穩(wěn)定,在線用戶峰值波動也比較大。如果是根據(jù)在線人數(shù)增長,動態(tài)新開群組,可能第一個(gè)群用戶已經(jīng)很多了,第二個(gè)群剛開始用戶比較少;或者,在峰值期間開了比較多的群,隨著熱度降低用戶離開,用戶變得分散,一些群的用戶可能較稀少,聊天互動較少,這時(shí)需要縮容合并群。如何平衡多個(gè)群的用戶,達(dá)到好的業(yè)務(wù)效果,也是比較難做的。
基于以上分析,我們并沒有選擇多群組方案。
2.4 組播mcast方案
支持實(shí)時(shí)高并發(fā)百萬量級同時(shí)在線用戶的直播消息架構(gòu),組播mcast方案的提出及演化。
2.4.1 跳出原有框架思考
是否要采用以上基于群聊的優(yōu)化方案,還是可以另辟蹊徑?
先暫時(shí)拋開群收發(fā)消息流程,對于消息下發(fā)來說,如果一定要說一個(gè)步驟是必不可少的,那一定是長連接下發(fā)這步了。沒有通過長連接下發(fā),消息就無法最終到達(dá)用戶;當(dāng)然有人說輪詢拉取也可以替代長連接下發(fā),來獲取消息,但顯然輪詢拉取的性能壓力和實(shí)時(shí)性與長連接下發(fā)相比差很多,故不在討論范圍。
如果能簡化為,給長連接服務(wù)下發(fā)消息時(shí)指定一個(gè)類似的groupID,長連接服務(wù)能直接拆分到所有群組用戶相關(guān)的長連接connect-j,就可以省略掉用戶列表拆分和動態(tài)路由查詢的百萬量級查詢。
這樣的話,消息下發(fā)的壓力將主要由長連接服務(wù)來承受,服務(wù)端也不需要對多個(gè)系統(tǒng)擴(kuò)容,直播消息的優(yōu)化可能會大為簡化。
根據(jù)這個(gè)思路,相當(dāng)于在長連接服務(wù)中,對連接connect也建立群組的概念。基于連接組的設(shè)想,我們設(shè)計(jì)了一套長連接的組播mcast機(jī)制。
2.4.2 長連接組播mcast基本概念
- 每個(gè)長連接組播mcast有全局唯一的標(biāo)識mcastID。
- 長連接組播mcast支持創(chuàng)建、刪除、修改、查詢等管理操作。
- 長連接組播mcast是若干長連接在線用戶的連接connect的集合。
- 一個(gè)用戶user-i在設(shè)備device-i-j上,對于特定應(yīng)用APP-k來說,建立唯一的一個(gè)長連接connect-j-k;(此處暫時(shí)不區(qū)別登錄用戶和非登錄用戶)。
- 長連接組播mcast與組內(nèi)長連接connect-j-k的關(guān)系維護(hù),不需要額外的獨(dú)立存儲,是維護(hù)在每個(gè)長連接服務(wù)的實(shí)例上。
2.4.3 長連接組播mcast的路由概念
組播mcast-m的路由route-m,是一個(gè)長連接服務(wù)實(shí)例的集合LcsList,記錄了所有加入mcast-m的長連接connect-i所在長連接服務(wù)實(shí)例lcs-j。
2.4.4 長連接組播mcast路由的記錄維護(hù)
加入組播mcast:
- 客戶端調(diào)用消息sdk加入mcast-m。
- 消息sdk通過長連接,發(fā)出上行請求mcastJoin(mcast-m)。
- 業(yè)務(wù)層收到來自長連接實(shí)例lcs-i上的連接connect-i的mcastJoin請求,校驗(yàn)mcast-m的合法性。
- 業(yè)務(wù)層請求路由層建立基于組播mcast-m的組播路由mcastRoute-m,將長連接實(shí)例lcs-i加入組播路由mcastRoute-m中。
- 業(yè)務(wù)層請求長連接服務(wù)層,請求mcastJoin所在長連接實(shí)例lcs-i,將請求所在連接connect-i加入到mcastConnectList-m中。
離開組播mcast,與加入組播mcast基本類似,由客戶端調(diào)用消息sdk離開mcast-m,發(fā)出上行請求mcastLeave(mcast-m),長連接服務(wù)端更新路由和mcastConnectList-m信息。
2.4.5 組播mcast消息推送
基于組播mcast的長連接消息推送過程,是一個(gè)1:M * 1:N的擴(kuò)散放大過程,具體過程描述如下:
- 一條消息msg-1推送,目的地是ID為mcast-m組播;
- 后端業(yè)務(wù)模塊根據(jù)目的mcast-m,做一致性hash選擇出mcast路由分發(fā)模塊實(shí)例mcastRouter- i,發(fā)送msg-1到mcastRouter-i;
- mcast分發(fā)路由模塊實(shí)例mcastRouter-i,根據(jù)mcast-m的組播路由mcastRoute-m,查找所對應(yīng)的接入實(shí)例路由記錄列表mcastLcsList-m,拆分出mcast-m所有的長連接接入實(shí)例lcs-1..lcs-M,分別并發(fā)發(fā)送msg-1到長連接實(shí)例上;
- 一個(gè)長連接服務(wù)實(shí)例lcs-j,收到消息msg-1推送后,根據(jù)組播mcast-m查找組播連接列表mcastConnectList-m,查出mcast-m內(nèi)所有的連接connect-m-1..connect-m-N,并發(fā)推送msg-1到消息客戶端sdk-m-1..sdk-m-N;
- 消息客戶端sdk-m-o收到msg-1后,遞交給上層業(yè)務(wù)(例如直播sdk)。
2.4.6 組播mcast機(jī)制的性能評估
現(xiàn)在分析一下以上的組播mcast機(jī)制的性能壓力:
- 組播mcast的路由維護(hù),主要壓力在于mcastJoin和mcastLeave,而Join的量級峰值請求很難超過2萬qps;訪問壓力比百萬低兩個(gè)數(shù)量級。
- 組播mcast的消息推送流程,在一級路由mcastRoute拆分到長連接實(shí)例時(shí),一般在幾十到百量級,成本很低。
- 組播mcast在長連接單實(shí)例內(nèi)的消息推送,是單進(jìn)程內(nèi)的多連接并發(fā)發(fā)送,經(jīng)優(yōu)化后線上實(shí)測,在單實(shí)例保持25W長連接的情況下,單實(shí)例壓測可達(dá)8Wqps的mcast穩(wěn)定下發(fā),保守按5Wqps容量評估;多個(gè)長連接實(shí)例間,是完全的并發(fā),可以較容易的水平擴(kuò)容。
-
綜上可知,對于100Wqps的下發(fā),20個(gè)長連接實(shí)例就可以完全負(fù)荷(20*5W=100W),且有一定裕量。如果500Wqps的下發(fā),也不超過100實(shí)例;1000W的下發(fā),如果以8W單實(shí)例較大的負(fù)荷承載,125實(shí)例就可以支持。
容量評估(實(shí)例數(shù)) | 單實(shí)例1萬qps | 單實(shí)例5萬qps | 單實(shí)例8萬qps |
100萬長連接 | 100 | 20 | 12.5 |
500萬長連接 | 500 | 100 | 62.5 |
1000萬長連接 | 1000 | 200 | 125 |
看上去,基于以上組播mcast機(jī)制,我們建立了一套高效的支持百萬量級QPS的長連接下發(fā)機(jī)制,當(dāng)前長連接服務(wù)的容量就可以支持,基本不用擴(kuò)容。但是否能完全滿足直播業(yè)務(wù)場景需求,還需要進(jìn)一步討論。
2.4.7 消息峰值問題
對于每秒1條消息,擴(kuò)散到100W用戶,甚至500W用戶,以上組播mcast機(jī)制似乎都能應(yīng)對。
但直播間內(nèi)消息的實(shí)際情況是,熱門的直播每秒用戶上行聊天消息會有很多,除聊天消息外,直播間還有人數(shù)、進(jìn)場、點(diǎn)贊、分享等定期和不定期發(fā)送的很多種類系統(tǒng)消息。
如果假設(shè)每秒峰值有100條各類消息,100W*100=1億,簡單按單實(shí)例5Wqps算,需要2000個(gè)實(shí)例才能支持,雖然比老的群聊系統(tǒng)應(yīng)該好很多,但系統(tǒng)還是遇到大量資源冗余或應(yīng)對峰值需要大量擴(kuò)容的老問題。是否能有更好的解決方式?
2.4.7.1 延時(shí)聚合
這里我們考慮常見的一個(gè)優(yōu)化思路,是通過批量聚合的模式來提高系統(tǒng)性能。如果將這100條消息,每秒聚合打包一次來統(tǒng)一下發(fā),QPS還是100W,長連接系統(tǒng)的下發(fā)QPS不變,但每秒下發(fā)消息量級可以達(dá)到1億,這個(gè)聚合方案實(shí)測是可行的。聚合模式,我們付出的成本是消息時(shí)延的上升,1秒的聚合平均時(shí)延增加500ms,用戶體驗(yàn)損失不算大,但系統(tǒng)下發(fā)消息量級可以提升百倍,綜合評估成本收益來看是合理的??紤]到直播的實(shí)際場景,大多數(shù)場景下秒級的聚合和時(shí)延是可以接受的。
2.4.8 消息帶寬問題
聚合延時(shí)下發(fā),長連接單實(shí)例QPS問題解決了,隨之而來的是,長連接單實(shí)例下發(fā)的帶寬壓力問題。例如,長連接單實(shí)例需要下發(fā)10000長連接時(shí),每秒100消息,消息平均2K字節(jié),實(shí)際帶寬為2K*100*10000*8=15625Mbps,這已經(jīng)超過單物理機(jī)的萬兆網(wǎng)卡的帶寬容量。
另一方面,從全局帶寬來看,也高達(dá)1.5Tbps,帶寬資源對于機(jī)房出口也會帶來壓力,這樣的帶寬成本過高,需要削減帶寬使用或有更好的替代方案。
-
壓縮
面對下發(fā)數(shù)據(jù)量帶寬消耗過大的問題,在不改動業(yè)務(wù)數(shù)據(jù)的前提下,我們采用了數(shù)據(jù)壓縮的解決方案。而壓縮是CPU密集型的操作,由于直播業(yè)務(wù)的實(shí)時(shí)性,不能簡單考慮壓縮比,在綜合平衡壓縮比、壓縮時(shí)延和壓縮CPU消耗后,調(diào)優(yōu)壓縮庫后實(shí)測的平均壓縮比達(dá)到6.7 : 1,數(shù)據(jù)量壓縮到原來的15%左右,這樣15625Mbps*15%=2344Mbps=2.29Gbps;單機(jī)萬兆網(wǎng)卡的帶寬容量,最多承載4.27萬的長連接下發(fā),雖然沒有達(dá)到5萬,基本也可以接受。
從全局帶寬來看,峰值也削減到不超過230Gbps,收益很明顯。
帶寬對比 | 壓縮前 | 壓縮后 |
單機(jī)10000長連接 | 15.26 Gbps | 2.29 Gbps |
全局100萬長連接 | 1.49 Tbps | 229 Gbps |
全局500萬長連接 | 7.45 Tbps | 1.12 Tbps |
2.4.9 客戶端性能問題
進(jìn)一步考慮,直播場景下,不僅是有較高的峰值消息量級,而是在直播過程中有持續(xù)的高消息量級壓力;這不僅對于服務(wù)端是壓力,對于客戶端來說也是個(gè)挑戰(zhàn)。持續(xù)的高消息量級,一方面,客戶端在接收、展示等方面有明顯的壓力;另一方面,直播界面上過多過快的消息刷新,對于用戶體驗(yàn)也是有害無益的。
-
限速
所以,在綜合平衡用戶體驗(yàn)和客戶端性能的基礎(chǔ)上,消息服務(wù)端增加了結(jié)合消息優(yōu)先級的分級頻控限速機(jī)制,單用戶客戶端并不需要承受每秒100條的壓力,削減每秒下發(fā)消息后,長連接單實(shí)例每秒下發(fā)5-8萬長連接,CPU和帶寬都是可以穩(wěn)定支持的。
2.4.10 實(shí)時(shí)消息問題
-
優(yōu)先級
我們提供了基于消息優(yōu)先級的實(shí)時(shí)下發(fā)機(jī)制,對于高優(yōu)消息可以立即觸發(fā)聚合下發(fā),不會增加聚合延時(shí);而對于普通中低優(yōu)消息,還是做延時(shí)聚合下發(fā)。
2.4.11 用戶在線問題
組播mcast機(jī)制的出發(fā)點(diǎn),在百萬量級高并發(fā)在線的場景下,保障在線用戶的消息到達(dá),允許不在線用戶接收消息的部分折損,付出合理的技術(shù)復(fù)雜度和成本,取得服務(wù)質(zhì)量和性能平衡。
而針對在線用戶的消息到達(dá),還有個(gè)關(guān)鍵問題是如何保障用戶的長連接在線。
為了提升長連接服務(wù)的接入穩(wěn)定性和可達(dá)性,我們在以下幾個(gè)方面做了優(yōu)化。
-
訪問點(diǎn)
長連接服務(wù)在國內(nèi)三大運(yùn)營商的華北華東華南區(qū)域均部署了接入點(diǎn)入口;針對有部分國外用戶的直播場景,增加了香港機(jī)房的獨(dú)立接入點(diǎn)入口。
-
HTTPDNS
針對部分用戶的DNS劫持問題和解析錯(cuò)誤問題,消息SDK接入了HTTPDNS服務(wù)并優(yōu)化本地緩存,形成多級域名解析保障體系,提升了域名解析的可靠性,減少了DNS劫持和錯(cuò)誤率。
-
心跳優(yōu)化
長連接的心跳是保活探活的重要手段,針對直播場景實(shí)時(shí)性高的特點(diǎn),為了盡快發(fā)現(xiàn)長連接斷鏈,在組播mcastJoin后,長連接心跳也調(diào)整為間隔更短、服務(wù)端動態(tài)可控的智能心跳。這樣在及時(shí)發(fā)現(xiàn)連接異常后,消息SDK可以快速主動重新建連。
-
斷鏈恢復(fù)
在直播間用戶已加入組播mcast的情況下,如果長連接斷鏈,長連接服務(wù)端會主動或被動的觸發(fā)清除組播mcast成員。而長連接重建連恢復(fù)時(shí),直播業(yè)務(wù)層也需要監(jiān)聽連接恢復(fù)信號,重新加入組播mcast,以恢復(fù)組播mcast的消息通路。
2.4.12 組播mcast小結(jié)
綜上所述,組播mcast機(jī)制,有效的解決了百萬量級同時(shí)在線用戶的消息實(shí)時(shí)下發(fā)問題;對于短時(shí)斷鏈和消息過多,允許部分消息的丟棄;滿足了直播場景消息的設(shè)計(jì)目標(biāo)。
組播mcast機(jī)制特點(diǎn)是:
- 消息服務(wù)和路由層壓力較輕,整體壓力只由長連接層承載,易于水平擴(kuò)容。
- 基于延時(shí)聚合下發(fā),輔以壓縮限速,可以很好的解決下行QPS與帶寬的性能問題。
- 系統(tǒng)整體下行的QPS和帶寬是完全可控的。100W在線用戶的下行最大QPS是100W,500W在線用戶的下行最大QPS是500W。單實(shí)例的下發(fā)能力5-8萬QPS是穩(wěn)定的。因此,可以很容易判斷整體的系統(tǒng)容量,特殊場景是否需要擴(kuò)容。
- mcast機(jī)制雖然是針對直播場景提出的,但本身設(shè)計(jì)具有通用性,可以應(yīng)用于其他需要實(shí)時(shí)在線大量用戶分組的消息推送場景。
在組播mcast機(jī)制解決了百萬量級的在線用戶實(shí)時(shí)消息下發(fā)后,直播消息的場景不斷擴(kuò)大,不斷有直播創(chuàng)新業(yè)務(wù)提出新的消息需求。相應(yīng)的,組播mcast的服務(wù)機(jī)制也需要與時(shí)俱進(jìn),不斷在深度和廣度上拓展優(yōu)化。以下重點(diǎn)介紹一下歷史消息和禮物消息。
3.1 歷史消息
對于剛進(jìn)入直播間的用戶來說,需要看到一些最近的聊天記錄,以增強(qiáng)聊天互動氛圍并幫助了解直播的進(jìn)展;對歷史聊天記錄感興趣額用戶,還可以追溯更多的消息歷史。這就產(chǎn)生了聊天歷史的需求。
為了支持這類歷史消息的需求,解決方案是對于每個(gè)組播mcast申請開通一個(gè)組播公共消息信箱mcast-mbox服務(wù)。
- 對于用戶消息和其他有持久化需要的消息,全部寫入這個(gè)消息信箱。
- 用戶可以指定組播mcastID,按時(shí)間區(qū)間和要拉取得消息條數(shù),來獲取組播mcast的歷史消息。
補(bǔ)充說明一下消息信息的概念和應(yīng)用。
3.1.1 消息信箱服務(wù)概念
- 消息信箱內(nèi)的一條消息msg,有唯一的消息標(biāo)識符msgID。
- 一條消息msg,還包括有發(fā)送方信息、接收方信息、消息類型、消息內(nèi)容等字段,此處可以暫時(shí)忽略。
- 每條消息可以設(shè)置過期時(shí)間,消息過期后不能訪問到。
- 每條消息可以設(shè)置已讀狀態(tài)。
- 一個(gè)消息信箱mbox,有唯一的信箱標(biāo)識符mboxID。
- 一個(gè)消息信箱mbox是一個(gè)容器,存儲有序的消息列表msgList;消息列表msgList按msgID排序的。
- 消息信箱服務(wù),對指定信箱mbox支持單條消息或批量消息的寫入。
- 消息信箱服務(wù),對指定信箱mbox支持基于msgID的單條消息或批量消息的查找。
- 消息信箱服務(wù),對指定信息mbox支持從msgID-begin到msgID-end的范圍查找。
3.2 禮物消息
禮物消息場景分析:
- 用戶送禮給主播,主播側(cè)需要盡快、可靠地收到禮物消息通知,才能及時(shí)的給予用戶反饋。
- 送出禮物的用戶,本地就可及時(shí)展示禮物效果,無消息通知強(qiáng)訴求。
- 直播間內(nèi)其他用戶,需要收到禮物消息,以展示禮物效果,提升直播間互動氛圍,激發(fā)其他用戶送禮。
- 禮物消息涉及用戶訂單和購買行為,需要由服務(wù)端確認(rèn)發(fā)出。
- 禮物消息優(yōu)先級明顯高于其他聊天消息、系統(tǒng)消息。
- 增加一個(gè)獨(dú)立的可靠消息組播mcast通道(如圖4中組播mcast-2),專供高優(yōu)可靠消息的收發(fā);與其他普通消息、系統(tǒng)消息在數(shù)據(jù)流層面隔離,減少相互干擾;
- 對于普通用戶側(cè)的端消息SDK,禮物消息組播mcast通道雖然是新增獨(dú)立通道,消息收發(fā)邏輯與普通消息組播mcast通道保持一致;
- 對于主播側(cè),端消息SDK對于禮物消息組播mcast通道,需要支持推拉結(jié)合模式,以保障禮物消息的全部到達(dá);即使有短暫的掉線,也需要取到全部禮物消息;
- 對于主播側(cè),在極端情況下,如果長連接建連有異常,消息SDK可以通過短連接接口輪詢,來拉取禮物組播mcast信箱消息來兜底。
基于以上獨(dú)立的可靠消息組播mcast通道方案,在未剔除一些異常場景的情況下,如主播下線未關(guān)播、數(shù)據(jù)偶發(fā)打點(diǎn)丟失等,禮物消息的觸達(dá)率已達(dá)到99.9%以上。
3.3 直播消息其他方面的發(fā)展
在百度直播的發(fā)展歷程中,直播消息服務(wù)還面臨著許多其他基礎(chǔ)性問題和創(chuàng)新業(yè)務(wù)帶來的其他挑戰(zhàn)?,F(xiàn)在這些問題都有了較好的解決方案,以下列舉一些,供大家學(xué)習(xí)參考:
- 如何支持多種客戶端場景,安卓、iOS、H5、小程序、PC。
- 如何支持同一場直播的消息在百度APP和好看、全民、貼吧等矩陣APP的打通。
- 如何支持非登錄用戶。IM一般是支持登錄用戶,而直播場景也需要支持非登錄用戶。
- 長連接服務(wù)如果出了嚴(yán)重問題,是否有端獲取消息的降級通道。
- 直播消息審核的機(jī)審人審如何做,如何支持先發(fā)后審和先審后發(fā)。
- 如何支持跨多個(gè)直播間的消息。
- 直播消息服務(wù)是如何支持創(chuàng)新業(yè)務(wù)的,如答題直播、直播帶貨、直播連麥等。
限于篇幅,以上問題在此不再做具體討論,有興趣同學(xué)歡迎直接聯(lián)系探討。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!