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