實時社群技術(shù)專題(二):百萬級成員實時社群技術(shù)實現(xiàn)(消息系統(tǒng)篇)
Posted on 2023-07-12 13:42 Jack Jiang 閱讀(89) 評論(0) 編輯 收藏本文由網(wǎng)易云信資深服務(wù)器開發(fā)工程師曹佳俊分享,原題“深度剖析“圈組”消息系統(tǒng)設(shè)計 | “圈組”技術(shù)系列文章”,為了提升內(nèi)容品質(zhì),本文有修訂和刪節(jié)。
1、引言
鑒于實時社群產(chǎn)品Discord在IM垂直應(yīng)用領(lǐng)域的爆火,類似的需求越來越多,云信的“圈組”就是針對這種應(yīng)用場景的技術(shù)產(chǎn)品。
“圈組”產(chǎn)品發(fā)布后獲得了很大的關(guān)注,很多云信用戶在接入SDK的同時對于“圈組”的底層技術(shù)細(xì)節(jié)和原理也非常關(guān)注,為此我們決定推出“圈組”相關(guān)的技術(shù)文章,分享云信在“圈組”技術(shù)設(shè)計上的一些思考和實踐。
本文是序列文章的第2篇,將要分享的是云信的實時社群產(chǎn)品“圈組”(“圈組”云信的類Discord產(chǎn)品實現(xiàn)方案)的消息系統(tǒng)技術(shù)設(shè)計實踐。

- 移動端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點此)
(本文已同步發(fā)布于:http://www.52im.net/thread-4321-1-1.html)
2、系列文章
本文是系列文章中的第 2 篇:
- 《實時社群技術(shù)專題(一):支持百萬人超級群聊,一文讀懂社群產(chǎn)品Discord》
- 《實時社群技術(shù)專題(二):百萬級成員實時社群技術(shù)實現(xiàn)(消息系統(tǒng)篇)》(* 本文)
- 《實時社群技術(shù)專題(三):百萬級成員實時社群技術(shù)實現(xiàn)(關(guān)系系統(tǒng)篇)》(稍后發(fā)布...)
3、作者介紹
曹佳俊:網(wǎng)易云信資深服務(wù)器開發(fā)工程師,畢業(yè)于中國科學(xué)院,碩士畢業(yè)后加入網(wǎng)易,負(fù)責(zé)云信 IM/RTC 信令等業(yè)務(wù)的服務(wù)器開發(fā)。專注于即時通訊、RTC 信令以及相關(guān)中間件等技術(shù),是云信開源項目 Camellia 的作者。
4、“圈組”的技術(shù)特點
在介紹“圈組”的技術(shù)細(xì)節(jié)之前,我們先了解一下圈組的技術(shù)特點。
“圈組”產(chǎn)品最大的特點是什么?
- 1)首先:是 server/channel 的二級結(jié)構(gòu);
- 2)其次:是構(gòu)建在二級結(jié)構(gòu)之上的大規(guī)模社群(單個 server 數(shù)十萬甚至上百萬成員);
- 3)以及:使用復(fù)雜的身份組系統(tǒng)來管理如此規(guī)模的社群組織和成員。
那么對于這樣一個新穎的 IM 系統(tǒng),在技術(shù)上應(yīng)該如何實現(xiàn)呢?
5、“圈組”和傳統(tǒng)IM群組的技術(shù)差異
5.1概述
一種簡單的思路是改造已有的 IM 系統(tǒng),對于“圈組”這樣的類 Discord 社群,第一個思路是拓展我們的群組功能,猛一看在很多方面確實挺像的。
我們做了個簡單的對比:

從上面的表格可以看到,“圈組”和群組最大的不同:
- 1)是容量的區(qū)別;
- 2)是二級結(jié)構(gòu)。
其他的諸如身份組、個性化推送策略,似乎只要適配的做一下就可以了。
那么是不是只要想辦法提升一下群組的容量,再在業(yè)務(wù)層封裝一下二級結(jié)構(gòu)就可以了呢?
答案顯然是否定的,或者至少說基于群組去擴(kuò)展不是一個很好的想法。
5.2二級結(jié)構(gòu)的差異
首先是二級結(jié)構(gòu)。
在類 Discord 的二級結(jié)構(gòu)中,成員的管理在 server 層,而 channel 成員是繼承自 server 的,而且在 channel 之上還有很多可見性的配置(我們的“圈組”提供了黑白名單機(jī)制,而Discord 則提供了查看頻道權(quán)限)。
在這種機(jī)制之下,任何 server 層面的成員變動,都可能影響全部或者部分頻道的成員列表。
面對這種復(fù)雜的結(jié)構(gòu),群組有兩種思路去實現(xiàn):
- 1)一種是 N 個群,邏輯上隸屬于同一個 server;
- 2)一種是一個群映射為一個 server。
不管哪種方式,先不說消息投遞這塊的邏輯,僅成員管理上邏輯的耦合和交織的復(fù)雜性,足以勸退任何人。
5.3容量的差異
常規(guī)IM群組的容量一般只有數(shù)百,最多可以擴(kuò)展到數(shù)千。
對于IM群組成員的管理,我們一般采取全量+增量同步相結(jié)合的方案,客戶端和服務(wù)器映射到相同的群組鏡像(群信息+群成員等)。此時很多操作,例如群成員的展示、檢索,消息的艾特等,都可以基于純客戶端進(jìn)行。
而“圈組”要求幾十萬甚至上百萬的容量,顯然客戶端無法一次性獲取到所有成員,如果你一次性加入多個 server,那成員的數(shù)量將更加膨脹。
因此在“圈組”這種大規(guī)模社群的設(shè)計中,很多邏輯都會轉(zhuǎn)向云端,此時不管是 SDK 還是服務(wù)器,均需要修改原有的設(shè)計邏輯。
5.4消息規(guī)模差異
此外,大規(guī)模社群帶來的是消息爆炸。
在原有的IM群組設(shè)計中,假設(shè)一個人同時加入了 1000 個群,那么這 1000 個群內(nèi)的所有消息均會在第一時間下發(fā)給給客戶端。
但是在一般的業(yè)務(wù)場景中,不會所有的群都同時活躍,假設(shè)這 1000 個群變成了 1000 個服務(wù)器/頻道,作為一種社群組織,同時活躍的可能性將大大增加,而且每個服務(wù)器/頻道的人數(shù)遠(yuǎn)遠(yuǎn)超過普通的群組,疊加之后帶來的消息爆炸現(xiàn)象在原有的群組體系中將帶來極大的壓力。
壓力包括多方面:
- 1)首先是海量消息的存儲壓力;
- 2)其次是海量消息在線廣播/離線消息推送帶來的帶寬和服務(wù)器壓力;
- 3)以及客戶端在面對大量消息沖擊時如何有效地接受和合理的展示。
5.5小結(jié)
除了容量、二級結(jié)構(gòu)、消息規(guī)模,包括身份組、成員管理、個性化推送策略等等都存在巨大差異。
是否真的適合在群組中添加這些復(fù)雜邏輯呢,強行綁定在一起會不會既沒有一個好用的類 Discord 平臺,也使得原始的群組功能繁雜,反而降低了易用性呢?
經(jīng)過上面的一些分析,我們基本可以得出一個結(jié)論:在已有的群組基礎(chǔ)上擴(kuò)展來實現(xiàn)一個類 Discord 功能的社群,顯然不是一個很好的思路。
那么還有其他“捷徑”嗎?
IM聊天室也是一個潛在的選項,聊天室的一大特點就是支持超大規(guī)模同時在線(參見《千萬級實時直播彈幕的技術(shù)實踐》),容量似乎已經(jīng)不是問題,但是當(dāng)考慮添加其他一些強社交關(guān)系的特性時(如成員、身份組等)就顯得有點為難了,聊天室本身就是來去自如的一個開放空間,這個和圈組的產(chǎn)品本身定位互相沖突的。
因此基于聊天室擴(kuò)展的方案也基本 pass 掉了。
6、“圈組”的技術(shù)難點
基于上述種種的思考和討論,最終選擇脫離已有 IM 體系,從零研發(fā)一套全新的社群方案“圈組”,“圈組”不是一個簡單的 IM 功能,而是一套可以獨立運行的 IM 系統(tǒng)。
經(jīng)過上面的討論,相信大家對“圈組”本身的技術(shù)特點和難點也有所理解。
可以歸納為以下幾點:
- 1)二級結(jié)構(gòu)下成員無上限的社交關(guān)系系統(tǒng)設(shè)計;
- 2)超大社群下消息系統(tǒng)設(shè)計;
- 3)復(fù)雜高效的身份組系統(tǒng)設(shè)計;
7、“圈組”技術(shù)實現(xiàn)之整體架構(gòu)
“圈組”整體架構(gòu):

上面展示了“圈組”服務(wù)整體的架構(gòu)。
可以看到整個“圈組”服務(wù)是一個分層的架構(gòu):
- 1)首先是接入層,包括 LBS 服務(wù)和長鏈接服務(wù)器以及 API 網(wǎng)關(guān),對應(yīng)客戶端 SDK 和用戶服務(wù)器;
- 2)后面是網(wǎng)絡(luò)層,包括大網(wǎng) WE-CAN 和協(xié)議路由服務(wù);
- 3)其次是服務(wù)層,劃分了多個服務(wù)模塊,每個模塊都包括多個微服務(wù);
- 4)最后是基礎(chǔ)設(shè)施。
8、“圈組”技術(shù)實現(xiàn)之消息系統(tǒng)架構(gòu)
這其中和消息系統(tǒng)相關(guān)聯(lián)的包括接入層、網(wǎng)絡(luò)層、以及后端的登錄/訂閱/消息/檢索等模塊。
基本架構(gòu)如下:

消息系統(tǒng)中第一個要討論的點就是消息的存儲和分發(fā)方式,包括在線廣播、離線推送、歷史消息三個維度。
下面幾節(jié)我們將對消息系統(tǒng)中各模塊分別展開介紹。
9、“圈組”消息系統(tǒng)技術(shù)實現(xiàn)1:在線廣播
對于一般的IM群組來說,在線廣播的一般過程是這樣的:依次查詢?nèi)航M里的所有人的在線狀態(tài),如果在線,則將消息發(fā)送給對應(yīng)的長鏈接服務(wù)器。
顯然這種機(jī)制無法復(fù)制到“圈組”,因為在“圈組”的一個服務(wù)器里可能存在超過 100w 的人。
此外:IM聊天室的廣播模式也不能直接復(fù)用,因為在聊天室架構(gòu)中,每個長鏈接映射到一個聊天室,因此當(dāng)你登錄到某個聊天室的時候,你只會收到該聊天室的消息。而對于“圈組”來說,每個用戶會同時加入多個服務(wù)器/頻道,而且會同時收到多個服務(wù)器/頻道的消息。

針對“圈組”的上述特點:我們設(shè)計了消息訂閱模式,也就是用戶登錄之后,需要訂閱感興趣的相關(guān)服務(wù)器/頻道,服務(wù)器會記錄下這個訂閱信息。當(dāng)有新消息的時候,服務(wù)器通過訂閱關(guān)系(而不是在線狀態(tài))查詢到需要廣播的列表,通過這種方式就不再需要遍歷服務(wù)器/頻道里的所有用戶。
但是當(dāng)一個服務(wù)器/頻道里在線人數(shù)非常多的時候,這個訂閱關(guān)系仍然是巨大的。
為此:我們設(shè)計了一種兩層訂閱模型,即所有的訂閱關(guān)系會保存在長鏈接服務(wù)器上(QChatLink/QChatWebLink),同時長鏈接服務(wù)器會定時發(fā)送心跳給后端的訂閱服務(wù)器,心跳信息相比原始的訂閱信息會大大簡化,比如長鏈接服務(wù)器上會記錄賬號 A 訂閱了某個頻道 A 的消息,如果有 1w 個賬號,則有 1w 條訂閱記錄,而心跳信息里只會上報有 1w 個人訂閱了某個頻道 A 的消息,具體的賬號列表則被精簡掉了。當(dāng)一條消息需要廣播時,消息服務(wù)會訪問訂閱服務(wù),獲取到該服務(wù)器/頻道被訂閱的長鏈接服務(wù)器列表,并依次給該列表中的長鏈接服務(wù)器發(fā)送消息下發(fā)通知,長鏈接服務(wù)器收到通知后會根據(jù)訂閱詳情再廣播給所有客戶端。
此外:我們還提供了多種訂閱類型,當(dāng)你非常關(guān)心某個頻道消息時(比如頁面正停留在該頻道),此時你可以訂閱該頻道的消息。對于其他頻道,如果你僅僅需要知道該頻道有多少條未讀消息(或者有無未讀消息),則可以選擇訂閱該頻道的未讀計數(shù)(或者未讀狀態(tài)),此時服務(wù)下發(fā)時僅會廣播精簡的消息體用于維護(hù)客戶端未讀計數(shù),并且當(dāng)未讀計數(shù)達(dá)到一定閾值之后(比如 99+),服務(wù)器可以選擇不再下發(fā)任何通知消息而不影響用戶體驗。
通過上文介紹的消息訂閱模型,極大地提高了超大型的圈組頻道/服務(wù)器消息在線廣播的效率,降低了服務(wù)器壓力。
除此之外:我們還設(shè)計了針對小型頻道的特殊策略,對于小型頻道,即使不訂閱,服務(wù)器也會下發(fā)消息通知給頻道里所有人,從而減輕端側(cè)消息訂閱模型的維護(hù)成本。針對消息訂閱機(jī)制本身,后續(xù)我們也會根據(jù)不同的業(yè)務(wù)場景,提供更多一站式的策略來幫助降低接入成本,提升整體的易用性。
10、“圈組”消息系統(tǒng)技術(shù)實現(xiàn)2:離線推送
在強社交的場景下,離線消息推送對于維持用戶粘性+提升產(chǎn)品體驗有很大的作用。
從技術(shù)角度看的話,主要解決2個問題:
1)第一個是超大型服務(wù)器/頻道的消息推送的效率問題;
2)另一個是提供足夠豐富的推送策略來幫助 C 端用戶,避免被過量的推送消息給打擾。
針對第一個問題,我們針對不同規(guī)模的服務(wù)器/頻道采取了不同的策略:
- 1)對于小型頻道:采用類似于群組的消息推送模型;
- 2)對于大型頻道:對于每一條需要推送的消息,會根據(jù)目標(biāo)用戶的 ID 進(jìn)行任務(wù)分片,多個節(jié)點并行操作,提高推送效率。
此外:分片會采用一致性策略,保證單個用戶固定為某些節(jié)點,從而提高緩存命中效率。
針對第二個問題,推送策略可以用以下幾句話來描述:
- 1)既關(guān)注促活,又保證不打擾;
- 2)大型 server 是游樂場,只推送與用戶相關(guān)的重要消息(如 @消息);
- 3)小型 server 是與朋友相處的小天地,支持消息的全部推送。
并且:未來用戶還可以自定義消息的高低優(yōu)先級,并搭配不同的推送配置(如不同的免打擾配置等),如下圖所示。

11、“圈組”消息系統(tǒng)技術(shù)實現(xiàn)3:歷史消息
歷史消息的存儲在“圈組”的場景中也需要一些特別的設(shè)計。
同樣以傳統(tǒng)IM群組為例,一般來說消息的存儲方式有兩種,寫擴(kuò)散和讀擴(kuò)散。在小型的IM群組或者多人會話中,寫擴(kuò)散模式可以簡化設(shè)計,但是當(dāng)群組規(guī)模擴(kuò)大到一定程度(如萬人群),讀擴(kuò)散就成了選擇。
而對于“圈組”這種單個服務(wù)器可能上百萬人的“群組”中,除了常規(guī)的讀擴(kuò)散之外,我們還設(shè)計了多級緩存的結(jié)構(gòu)來應(yīng)對海量的讀請求。
基本的存儲架構(gòu)大致如下:

消息的存儲主要包括兩部分:
- 1)一部分是消息本身;
- 2)一部分是未讀計數(shù)。
首先是寫入:對于上述兩者,我們都會使用中心化的緩存服務(wù)器來存儲最近的數(shù)據(jù),并使用異步+批量+聚合等手段,通過 MQ 異步落庫,從而平衡寫入效率(單條寫入性能低)和寫入讀取延遲(異步寫入有延遲)的問題,并且針對不同數(shù)據(jù)類型的特點,我們也選擇了不同的存儲方案(歷史消息使用分布式時間序列數(shù)據(jù)庫,未讀計數(shù)使用分布式 k-v 數(shù)據(jù)庫),最大化地提升消息存儲和查詢的性能和效率。
有寫就有讀,針對讀取操作:
- 1)所有最近的消息和未讀計數(shù)均會存儲在中心化緩存中,并通過先進(jìn)先出和緩存過期等不同的策略來確保緩存中存儲的永遠(yuǎn)是最新和最熱的數(shù)據(jù);
- 2)對于消息 ID 和消息內(nèi)容本身,中心化緩存中也會有不同的數(shù)據(jù)結(jié)構(gòu)和過期策略,來平衡緩存命中率和緩存容量消耗;
- 3)當(dāng)緩存過期了,如果有關(guān)聯(lián)的讀寫請求,將會觸發(fā)緩存的重建,以保證緩存的命中率始終保持在較高水位;
- 4)當(dāng)有高頻的讀請求,還會觸發(fā)熱點 cache 的檢測,并將一部分讀請求下沉到各個計算節(jié)點的內(nèi)存中,以應(yīng)對突發(fā)流量的沖擊。
上述針對“圈組”的特別設(shè)計,消息存儲系統(tǒng)可以應(yīng)對幾十?dāng)?shù)百人的小型圈組頻道,也可以從容應(yīng)對上百萬的超大型頻道。
12、相關(guān)資料
[1] IM群聊消息究竟是存1份(即擴(kuò)散讀)還是存多份(即擴(kuò)散寫)?
[2] 網(wǎng)易云信技術(shù)分享:IM中的萬人群聊技術(shù)方案實踐總結(jié)
[3] 企業(yè)微信的IM架構(gòu)設(shè)計揭秘:消息模型、萬人群、已讀回執(zhí)、消息撤回等
[4] 融云IM技術(shù)分享:萬人群聊消息投遞方案的思考和實踐
[5] 微信直播聊天室單房間1500萬在線的消息架構(gòu)演進(jìn)之路
[6] 百萬人在線的直播間實時聊天消息分發(fā)技術(shù)實踐
[8] 深度解密釘釘即時消息服務(wù)DTIM的技術(shù)設(shè)計
[9] 深度揭密RocketMQ在釘釘IM系統(tǒng)中的應(yīng)用實踐
[10] 一套億級用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[11] 一套億級用戶的IM架構(gòu)技術(shù)干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等
[12] 從新手到專家:如何設(shè)計一套億級消息量的分布式IM系統(tǒng)
(本文已同步發(fā)布于:http://www.52im.net/thread-4321-1-1.html)
作者:Jack Jiang (點擊作者姓名進(jìn)入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時通訊開發(fā)交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時是【原創(chuàng)Java
Swing外觀工程BeautyEye】和【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文
歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處(也可前往 我的52im.net 找到我)。