揭秘百度IM消息中臺(tái)的全量用戶消息推送技術(shù)改造實(shí)踐

本文內(nèi)容由百度技術(shù)團(tuán)隊(duì)分享,原題“基于公共信箱的全量消息實(shí)現(xiàn)”,為了幫助理解,有較多修訂、內(nèi)容重組和重新排版。
1、引言
百度的IM消息中臺(tái)為百度APP以及廠內(nèi)百度系產(chǎn)品提供即時(shí)通訊的能力,提供包括私聊、群聊、聊天室、直播彈幕等用戶溝通場(chǎng)景,并幫助業(yè)務(wù)通過(guò)消息推送觸達(dá)用戶。
如今,百度APP新增了一種需要以“低用戶打擾”的形式觸達(dá)全量用戶的場(chǎng)景需求,而現(xiàn)有的IM消息中臺(tái)主要是基于用戶“私有信箱”通知拆分的機(jī)制(通俗了說(shuō)也就是IM里的“擴(kuò)散寫(xiě)”),所以如果不進(jìn)行改造,是很難低成本、高時(shí)效的滿足該場(chǎng)景訴求。
基于上述問(wèn)題,本文介紹了百度現(xiàn)有IM消息中臺(tái)系統(tǒng)的主要組成,并對(duì)比多種實(shí)現(xiàn)方案的優(yōu)劣,以“公有信箱”通知讀擴(kuò)散的技術(shù)方案對(duì)現(xiàn)有IM消息中臺(tái)系統(tǒng)進(jìn)行改造,從而達(dá)成了低成本、高時(shí)效地實(shí)現(xiàn)全量用戶通知推送需求。

技術(shù)交流:
- 移動(dòng)端IM開(kāi)發(fā)入門(mén)文章:《新手入門(mén)一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM》
- 開(kāi)源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點(diǎn)此)
(本文已同步發(fā)布于:http://www.52im.net/thread-4235-1-1.html)
2、全量用戶消息推送需求背景
百度APP新增了需要通過(guò)IM實(shí)時(shí)通知觸達(dá)全量用戶的訴求,比如2022年12月7日解除疫情管控結(jié)束后,將經(jīng)過(guò)篩選的官方政策解讀、專(zhuān)題匯總、知識(shí)科普、實(shí)用工具類(lèi)介紹等信息,通過(guò)官方號(hào)“x度小助手”下發(fā)觸達(dá)到百度APP用戶,從而來(lái)有效體現(xiàn)人文關(guān)懷,提高用戶粘性。
在以IM消息服務(wù)進(jìn)行全量用戶消息觸達(dá)時(shí),需要滿足以下訴求:

具體就是:
1)在觸達(dá)范圍上:希望盡量擴(kuò)大用戶觸達(dá)范圍,包括百度APP月活用戶、以及非月活用戶但是近期新注冊(cè)或登錄的用戶;
2)在時(shí)效上:一次全量觸達(dá),希望短時(shí)間內(nèi)完成(比如小時(shí)級(jí)、甚至分鐘級(jí)),搶占時(shí)效性;
3)在用戶打擾方面:消息觸達(dá)不能給用戶帶來(lái)較大的打擾,每次消息下發(fā),只觸達(dá)一次,不能重復(fù)打擾用戶(但是需要保留回訪入口,滿足用戶二次查看的訴求)。
3、現(xiàn)有IM消息中臺(tái)的技術(shù)痛點(diǎn)
我們現(xiàn)有的IM(即時(shí)通訊)服務(wù)中,每個(gè)IM用戶對(duì)應(yīng)一個(gè)用戶信箱。
基于現(xiàn)有的IM技術(shù)實(shí)現(xiàn)方案,如果想完成全量用戶的消息觸達(dá),需要把消息推送到每個(gè)用戶的信箱(也就是IM中的擴(kuò)散寫(xiě))。
這樣的話,要完成6億以上的消息寫(xiě)入(假定每條占用存儲(chǔ)4KB,每秒寫(xiě)入2W條消息),在消息寫(xiě)入時(shí)效性以及存儲(chǔ)資源消耗上,都是很難接受的。
且現(xiàn)有的基于用戶私有信箱的方案,在同時(shí)支持多條全量用戶通知消息的場(chǎng)景下,擴(kuò)展性也較差。
基于上述需求背景和技術(shù)痛點(diǎn),我們本次的改造目的,就是要找到一種技術(shù)方案,從而在特定業(yè)務(wù)場(chǎng)景下通過(guò)改造后的消息服務(wù),低成本、高時(shí)效的給全量用戶推送內(nèi)容一致的消息通知。
4、現(xiàn)有IM消息中臺(tái)的主要技術(shù)實(shí)現(xiàn)
在討論改造方案前,我們有必要介紹一下目前IM消息系統(tǒng)的現(xiàn)狀,包括消息系統(tǒng)的組成、通知拉取模式、用戶信箱等。
4.1 消息系統(tǒng)組成
從普通用戶的直觀體驗(yàn)上看,一個(gè)IM系統(tǒng)可以包括如下元素:
1)用戶主體;
2)用戶賬號(hào);
3)賬號(hào)關(guān)系;
4)聊天會(huì)話;
5)聊天消息。
用自然語(yǔ)言串一下以上元素就是:
1)“用戶主體”具有“用戶賬號(hào)”;
2)“用戶主體”具有頭像、昵稱(chēng)等用戶屬性;
3)“用戶主體”通過(guò)“用戶賬號(hào)”登錄IM系統(tǒng),進(jìn)行聊天;
4)“用戶賬號(hào)”之間的關(guān)注、屏蔽、免打擾等構(gòu)成“用戶關(guān)系”;
5)通過(guò)用戶之間的互動(dòng)環(huán)節(jié)可以產(chǎn)生“聊天消息”;
6)聊天記錄構(gòu)成了一個(gè)“聊天會(huì)話”。
下面這張圖可能更直觀一些:

從集成消息服務(wù)的業(yè)務(wù)方角度看:
1)一個(gè)IM系統(tǒng)可以包括消息客戶端(消息客戶端UI組件、消息SDK)和消息服務(wù)端;
2)IM消息可以作為一種服務(wù),嵌入到各業(yè)務(wù)系統(tǒng)中,為業(yè)務(wù)系統(tǒng)提供“實(shí)時(shí)交互”能力;
3)業(yè)務(wù)通過(guò)集成IM服務(wù),提升其用戶體驗(yàn);
4)業(yè)務(wù)APP集成IM SDK,通過(guò)IM SDK與IM Server交互,完成用戶上行通訊能力;
5)業(yè)務(wù)APP Server通過(guò)與IM Server交互,完成通知下行觸達(dá)用戶。
下圖為一個(gè)集成了IM SDK的業(yè)務(wù)架構(gòu)圖:

從使用場(chǎng)景來(lái)看,消息包括:
1)“私信消息”(包括用戶上下行消息);
2)“通知消息”(業(yè)務(wù)方給用戶推送的下行消息);
3)“群聊”、“聊天室”;
4)“直播間彈幕”等。
4.2 消息的通知拉取模式
百度的IM消息系統(tǒng),采用通知拉?。╪otify-pull)模式來(lái)感知新消息、拉取新消息。
IM SDK登錄時(shí),與IM 服務(wù)端建立長(zhǎng)連接(LCS, Long Connect Service),用戶有新的消息時(shí),通過(guò)長(zhǎng)連接下發(fā)notify,實(shí)時(shí)通知用戶的IM SDK。
實(shí)時(shí)notify不寫(xiě)用戶信箱,因?yàn)閚oitfy不是消息(可以理解為提醒在線用戶有新消息的信號(hào)),IM SDK根據(jù)這個(gè)信號(hào),來(lái)服務(wù)端拉取消息。
業(yè)務(wù)方server或者其他用戶給該用戶發(fā)送消息后,經(jīng)過(guò)IM業(yè)務(wù)處理模塊,把消息寫(xiě)入接收者信箱,IM Server會(huì)根據(jù)用戶的登錄和路由信息,給消息接收者(私信場(chǎng)景下也包括“消息發(fā)送者”,用于消息的多端同步)發(fā)送新消息notify,接收到notify的IM設(shè)備,通過(guò)IM SDK來(lái)IM Server端拉?。╬ull)消息。
4.3 用戶信箱介紹
為了暫存尚未拉取到IM SDK本地的離線消息,需要對(duì)消息進(jìn)行服務(wù)端存儲(chǔ),而消息的離線存儲(chǔ)是通過(guò)消息信箱服務(wù)完成的。
目前百度的IM用戶消息信箱主要包括:
1)用戶私有信箱;
2)群公共信箱(非下文提到的用戶公共信箱);
3)直播間彈幕mcast等。
用戶信箱通過(guò)“消息所屬應(yīng)用”+“IM標(biāo)識(shí)用戶的唯一ID”來(lái)標(biāo)識(shí)。
就一條消息而言:消息參與者有“消息發(fā)送者”和“消息接收者”,消息收發(fā)雙方的信箱都是相互獨(dú)立的(假設(shè)發(fā)送方刪除了自己信箱的某一條消息,不會(huì)影響消息接受者信箱的消息)。
對(duì)于有查看歷史消息訴求的一方來(lái)說(shuō):消息需要入該方的信箱,比如用戶之間的私信(也就是一對(duì)一單聊)消息需要入發(fā)送者和接收者的信箱。
而對(duì)于全量用戶消息通知的場(chǎng)景:消息不需要存儲(chǔ)發(fā)送者信箱,而只需要存接收者的信箱。而用戶的信箱排序,是基于信箱Timeline(詳見(jiàn)《現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討》)。即消息在信箱內(nèi)部基于時(shí)間線存儲(chǔ),每條消息對(duì)應(yīng)一個(gè)unix 微秒時(shí)間戳(如第一條消息1679757323320865),用戶進(jìn)行信箱拉取時(shí),基于時(shí)間范圍正序或者逆序拉取。
如下為信箱Timeline的示例:

用戶信箱中的每一條消息記錄都包含四個(gè)主要部分:
1)“消息ID”;
2)“消息用戶標(biāo)識(shí)”;
3)“消息通用屬性”;
4)“消息業(yè)務(wù)屬性”。
下面詳細(xì)介紹以上四個(gè)部分:
1)消息ID:為unix微秒時(shí)間戳,不需要全局唯一,只需要特定用戶信箱范圍內(nèi)唯一即可;
2)消息用戶標(biāo)識(shí):包括from_uid、to_uid、contacter;
3)消息通用屬性:包括create_time、expire、is_read;
4)消息業(yè)務(wù)屬性:包括category、type、priority、business_type、APP_id、msgkey、content等。
如下為一條消息記錄示例:

5、全量用戶消息推送技術(shù)方案選型
5.1 需求分析
目前百度的IM消息推送機(jī)制中,主要支持:
1)單播:消息推送方式,每次給一個(gè)用戶推送一條消息;
2)批量單播:每次給小范圍用戶推送消息,比如30個(gè);
3)廣播:基于關(guān)注關(guān)系的推送,如給全量粉絲推送。
上述三種消息推送機(jī)制推送的消息,均需要存儲(chǔ)服務(wù)端的用戶私有信箱。為了完成百度APP 6億以上全量月活用戶的消息推送,目前有三種可選的方案,接下來(lái)我們逐一分析。
5.2 方案1:全流程從通知入口推送
該種方式下:需要獲取全量的月活用戶列表,經(jīng)過(guò)IM Server推送入口,給每一個(gè)用戶推送疫情相關(guān)通知。
該通知寫(xiě)入到用戶信箱時(shí):
1)若用戶在線,在實(shí)時(shí)拉取該通知;
2)若用戶離線,再下次登錄IM服務(wù)時(shí),拉取離線通知。
該種方案下:推送行為會(huì)覆蓋IM的全流程,推送的通知會(huì)進(jìn)入每個(gè)月活用戶的私有信箱,服務(wù)壓力大。其中增量用戶不會(huì)收到通知推送(這里增量用戶指的是不在月活用戶列表的用戶)。
5.3 方案2:跳過(guò)通知入口直接寫(xiě)信箱
該種方式跳過(guò)IM消息推送流程中的中間環(huán)節(jié),直接把通知消息寫(xiě)入用戶信箱。
由于跳過(guò)了中間流程直接寫(xiě)入信箱,通知寫(xiě)入速度主要取決于信箱底層存儲(chǔ)的壓力承受情況。
該種方案下,同方案1一樣,無(wú)法給用戶發(fā)送實(shí)時(shí)通知,依賴(lài)用戶IM SDK的主動(dòng)消息拉?。〝噫満笾匦碌卿?新消息提醒拉取),無(wú)法給增量用戶發(fā)送通知。
該方案由于跳過(guò)中間環(huán)節(jié)直接寫(xiě)信箱,風(fēng)險(xiǎn)較大,無(wú)法直接提供給業(yè)務(wù)方使用,不建議如此操作。
5.4 方案3:公有信箱實(shí)現(xiàn)機(jī)制
該種公有信箱機(jī)制的邏輯是把通知消息寫(xiě)入“公共信箱”。在用戶消息拉取時(shí),合并“用戶私信信箱”+“公共信箱”的消息。
5.5 三種方案比較

方案1和2都是寫(xiě)擴(kuò)散方式,基于現(xiàn)有“用戶私有信箱”的機(jī)制,把通知消息寫(xiě)入每個(gè)接收通知的用戶私有信箱。
方案2與方案1的差別主要是跳過(guò)了消息中間流程,可以避免因?yàn)橹虚g環(huán)節(jié)負(fù)載瓶頸導(dǎo)致整體消息寫(xiě)入速度過(guò)低。
方案3是讀擴(kuò)散方式,消息不用再寫(xiě)入接收通知的用戶私有信箱,而只需要在公共信箱存儲(chǔ)一份。在用戶拉取消息時(shí),實(shí)時(shí)拉取公共信箱的消息。方案③中可以采用內(nèi)存緩存方案,解決對(duì)公共信箱的讀壓力。
本質(zhì)上來(lái)說(shuō):方案3與方案前兩種相比,是用讀成本(CPU)換寫(xiě)成本(存儲(chǔ))。
6、基于公有信箱技術(shù)方案的全量用戶消息推送實(shí)現(xiàn)
6.1 概述
基于上述方案3的思路,我們進(jìn)行基于公有信箱的全量消息設(shè)計(jì)與實(shí)現(xiàn)。
該種方案中包含兩個(gè)主要流程:
1)全量消息的管理;
2)用戶私有+公有信箱的拉取。
6.2 全量消息的管理
全量消息管理主要分為:
1)運(yùn)營(yíng)O端操作平臺(tái):復(fù)用運(yùn)營(yíng)消息平臺(tái);
2)全量消息處理服務(wù):復(fù)用IM服務(wù)的連接層、邏輯處理層、信箱代理、信箱處理。
運(yùn)營(yíng)O端平臺(tái)為運(yùn)營(yíng)同學(xué)提供可視化界面,可以對(duì)全量消息進(jìn)行編輯、預(yù)發(fā)布、發(fā)布、修改、停止、撤回等操作。
具體就是:
1)接入層:對(duì)接運(yùn)營(yíng)O端,進(jìn)行參數(shù)校驗(yàn)、轉(zhuǎn)發(fā)IM后端邏輯處理模塊;
2)邏輯處理層:進(jìn)行全量消息的創(chuàng)建、修改、停止、刪除、撤回等邏輯操作;
3)信箱代理層:復(fù)用IM服務(wù)的信箱CRUD操作;信箱存儲(chǔ)層公共信箱的底層存儲(chǔ)。
全量消息管理流程:

6.3 用戶信箱拉取
用戶通過(guò)IM SDK,以長(zhǎng)連接的方式,在邏輯處理層進(jìn)行消息拉拉取。
在用戶拉取信箱消息時(shí),需要對(duì)“用戶個(gè)人信箱”和“公有信箱”進(jìn)行合并。于是每次用戶信箱拉取,都需要進(jìn)行信箱的合并拉取。
6.3.1)公共信箱內(nèi)存緩存機(jī)制:
百度APP的IM用戶,在IM SDK登錄時(shí)需要拉取信箱中的消息。每次消息拉取時(shí),需要檢查公共信箱中是否有消息。
因此,公共信箱需要能抗住日常和峰值流量(拉取峰值為4.7Wqps)。為了防止流量擊穿,流量打到底層的持久化公共信箱MYSQL存儲(chǔ),我們?cè)O(shè)計(jì)了基于內(nèi)存的公共信箱緩存機(jī)制。同時(shí)公共信箱內(nèi)容變化時(shí),也要實(shí)時(shí)(或者在能容忍的范圍內(nèi)做到準(zhǔn)實(shí)時(shí))變更內(nèi)存緩存信箱中的消息,我們采用Bthread定期輪詢(xún)持久化公共信箱,更新內(nèi)存公共信箱,輪詢(xún)間隔可配置(比如設(shè)置1秒)。
6.3.2)分級(jí)發(fā)布機(jī)制:
同時(shí),在邏輯層實(shí)現(xiàn)白名單機(jī)制,支持全量消息在“預(yù)發(fā)布”狀態(tài)下,僅對(duì)白名單用戶可見(jiàn),從而達(dá)到分級(jí)驗(yàn)證的效果。
白名單的用戶列表通過(guò)邏輯處理成的配置加載,也支持通過(guò)CURL請(qǐng)求動(dòng)態(tài)修改白名單的配置。

7、基于公有信箱技術(shù)方案的技術(shù)挑戰(zhàn)
公有信箱的技術(shù)方案,需要解決如下問(wèn)題:

8、基于公有信箱技術(shù)方案的優(yōu)缺點(diǎn)總結(jié)
8.1 優(yōu)點(diǎn)
以公共信箱的方式,實(shí)現(xiàn)全量用戶消息分發(fā),具有:“分發(fā)速度快”、“資源成本低”的特點(diǎn)。

8.2 缺點(diǎn)
但公共信箱的方式也存在一定的局限性。
8.2.1)不適用于個(gè)性化要求高的場(chǎng)景:
由于消息在公共信箱只存儲(chǔ)一份,下發(fā)消息內(nèi)容固定,無(wú)法很大程度下,下發(fā)個(gè)性化消息(當(dāng)然也不是一定無(wú)法下發(fā)個(gè)性化的消息,可以通過(guò)在公共信箱存儲(chǔ)消息模板,根據(jù)拉取消息的用戶ID獲取個(gè)性化信息,在消息拉取時(shí),臨時(shí)拼裝消息,這樣就增大了消息拉取時(shí)的代價(jià))。
8.2.2)不適用于實(shí)時(shí)消息提醒場(chǎng)景:
1)從業(yè)務(wù)場(chǎng)景上看:全量消息優(yōu)先級(jí)低,不需要在全量生效的瞬間讓用戶感知。
2)從實(shí)現(xiàn)上看:全量消息實(shí)時(shí)消息提醒成本高。因?yàn)閷?shí)時(shí)消息提醒Notify,需要以類(lèi)似單播的形式實(shí)時(shí)通知用戶。和單播的區(qū)別是,Notify不用觸達(dá)離線用戶,也就是不用寫(xiě)用戶信箱,只需實(shí)時(shí)觸達(dá)在線用戶。
3)從系統(tǒng)壓力看:全量在線用戶均收到實(shí)時(shí)新消息提醒,會(huì)帶來(lái)信箱拉取請(qǐng)求的瞬時(shí)流量(手機(jī)百度IM SDK長(zhǎng)連接峰值在線1550W,假定新消息提醒在瞬間下發(fā),同時(shí)在線用戶信箱拉取請(qǐng)求,會(huì)把db打掛的)。
9、基于公有信箱技術(shù)方案的落地實(shí)施效果
全量消息目前已經(jīng)在百度APP得到應(yīng)用,包括:重大通知的下發(fā);百度APP功能更新介紹通知;消息的撤回,后續(xù)還將推廣到其他的矩陣APP的全量通知推送場(chǎng)景。
舉個(gè)具體的例子:22年Q4宣布疫情解封時(shí),利用全量消息推送,低成本、高時(shí)效的完成3條“疫情解封專(zhuān)項(xiàng)”全量消息下發(fā)。

?
在這個(gè)例子中,三次全量消息下發(fā),到達(dá)數(shù)據(jù)在2億+(該值小于月活的6億+),主要因?yàn)閹讉€(gè)原因:
1)本次全量消息有效期僅3天左右,全量消息有效期內(nèi)登錄IM SDK的用戶才有機(jī)會(huì)拉到全量消息;
2)本次下發(fā)使用了新的消息展示模板,所以限制了拉取全量消息的百度APP版本,只有高版本百度APP可以拉到;
3)本次全量消息,限制了僅有百度APP登錄用戶拉取。
10、未來(lái)展望
本文介紹了現(xiàn)有IM消息中臺(tái)系統(tǒng),并通過(guò)公有信箱技術(shù)方案的改造,達(dá)成了低成本、高分發(fā)速度完成全量用戶消息下發(fā)的設(shè)計(jì)、實(shí)現(xiàn)與應(yīng)用。
在全量用戶消息應(yīng)用方面,除了業(yè)務(wù)上的使用,后續(xù)也可以用于廣播消息、批量單播消息的撤回。比如由于誤操作發(fā)送了廣播消息,用戶已經(jīng)把廣播消息拉到了端,并持久化到端,這是可以“以全量消息的方式,下發(fā)刪除指令”,刪除已經(jīng)緩存到端的垃圾消息。
我們希望,通過(guò)消息系統(tǒng)持續(xù)不斷優(yōu)化,為更多的業(yè)務(wù)提供低成本、高穩(wěn)定性的即時(shí)通訊能力。
11、相關(guān)資料
[1]?現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討
[2]?百度APP移動(dòng)端網(wǎng)絡(luò)深度優(yōu)化實(shí)踐分享(一):DNS優(yōu)化篇
[3]?百度APP移動(dòng)端網(wǎng)絡(luò)深度優(yōu)化實(shí)踐分享(二):網(wǎng)絡(luò)連接優(yōu)化篇
[4]?百度APP移動(dòng)端網(wǎng)絡(luò)深度優(yōu)化實(shí)踐分享(三):移動(dòng)端弱網(wǎng)優(yōu)化篇
[5]?百度直播的海量用戶實(shí)時(shí)消息系統(tǒng)架構(gòu)演進(jìn)實(shí)踐
[6]?深入了解百度開(kāi)源的分布式RPC框架brpc的方方面面
[7]?百度網(wǎng)盤(pán)千萬(wàn)節(jié)點(diǎn)的P2P架構(gòu)設(shè)計(jì)(PPT)
[8]?零基礎(chǔ)IM開(kāi)發(fā)入門(mén)(一):什么是IM系統(tǒng)?
[9]?一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)
[10]?一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案
[11]?一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[12]?基于實(shí)踐:一套百萬(wàn)消息量小規(guī)模IM系統(tǒng)技術(shù)要點(diǎn)總結(jié)
[13]?一套十萬(wàn)級(jí)TPS的IM綜合消息系統(tǒng)的架構(gòu)實(shí)踐與思考
[14]?從新手到專(zhuān)家:如何設(shè)計(jì)一套億級(jí)消息量的分布式IM系統(tǒng)
[15]?閑魚(yú)億級(jí)IM消息系統(tǒng)的架構(gòu)演進(jìn)之路
[16]?深度解密釘釘即時(shí)消息服務(wù)DTIM的技術(shù)設(shè)計(jì)
[17]?一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐
[18]?企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬(wàn)人群、已讀回執(zhí)、消息撤回等
(本文已同步發(fā)布于:http://www.52im.net/thread-4235-1-1.html)