本文由融云技術團隊原創分享,原題“IM 消息同步機制全面解析”,為使文章更好理解,對內容進行了重新歸納和細節修訂。
1、內容概述
即時通訊(IM)系統最基礎、最重要的是消息的及時性與準確性,及時體現在延遲,準確則具體表現為不丟、不重、不亂序。
綜合考慮業務場景、系統復雜度、網絡流量、終端能耗等,我們的億級分布式IM消息系統精心設計了消息收發機制,并不斷打磨優化,形成了現在的消息可靠投遞機制。
整體思路就是:
- 1)客戶端、服務端共同配合,互相補充;
- 2)采用多重機制,從不同層面保障;
- 3)拆分上下行,分別處理。
本文根據融云億級IM消息系統的技術實踐,總結了分布式IM消息的可靠投遞機制,希望能為你的IM開發和知識學習起到拋磚引玉的作用。
(本文已同步發布于:http://www.52im.net/thread-3638-1-1.html)
2、推薦閱讀
以下是相關文章匯總,有興趣可以一并閱讀:
《零基礎IM開發入門(二):什么是IM系統的實時性?》
《零基礎IM開發入門(三):什么是IM系統的可靠性?》
《零基礎IM開發入門(四):什么是IM系統的消息時序一致性?》
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《IM開發干貨分享:如何優雅的實現大量離線消息的可靠投遞》
《理解IM消息“可靠性”和“一致性”問題,以及解決方案探討》
《如何保證IM實時消息的“時序性”與“一致性”?》
《IM群聊消息如此復雜,如何保證不丟不重?》
《從客戶端的角度來談談移動端IM的消息可靠性和送達機制》
《一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等》
《從新手到專家:如何設計一套億級消息量的分布式IM系統》
《淺談移動端IM的多點登錄和消息漫游原理》
《完全自已開發的IM該如何設計“失敗重試”機制?》
《IM開發干貨分享:我是如何解決大量離線消息導致客戶端卡頓的》
以下是融云技術團隊分享的其它文章:
《IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略》
《融云技術分享:基于WebRTC的實時音視頻首幀顯示時間優化實踐》
《融云技術分享:融云安卓端IM產品的網絡鏈路保活技術實踐》
《即時通訊云融云CTO的創業經驗分享:技術創業,你真的準備好了?》
3、客戶端與服務端消息交互整體原理
3.1 概述
一個完整的IM消息交互邏輯,通常會為兩段:
- 1)消息上行段:即由消息發送者通過IM實時通道發送給服務端;
- 2)消息下行段:由服務端按照一定的策略送達給最終的消息接收人。
3.2 消息上行段
消息上行段主要就是依賴IM的實時通道將消息傳遞給服務端。
這個階段的消息可靠投遞,需要從協議層進行保證,協議層需要提供可靠、有序的雙向字節流傳輸,我們是通過自研的通信協議 RMTP(即 RongCloud Message Transfer Protocol)實現的。
客戶端與服務端之間使用長連接,基于 RMTP 協議傳輸數據。
RMTP協議交互示意圖:
如上圖所示,協議層通過 QoS、 ACK 等機制,保證IM消息上行段數據傳輸的可靠性。
3.3 消息下行段
經過總結,消息下行段主要有三種行為。
1)客戶端主動拉取消息,主動拉取有兩個觸發方式:
- ① 拉取離線消息:與 IM 服務新建立連接成功,用于獲取不在線的這段時間未收到的消息;
- ② 定時拉取消息:在客戶端最后收到消息后啟動定時器,比如 3-5 分鐘執行一次。主要有兩個目的,一個是用于防止因網絡、中間設備等不確定因素引起的通知送達失敗,服務端客戶端狀態不一致,一個是可通過本次請求,對業務層做狀態機保活。
2)服務端主動-發送消息(直發消息):
這是在線消息發送機制之一,簡單理解為服務端將消息內容直接發送給客戶端,適用于消息頻率較低,并且持續交互,比如二人或者群內的正常交流討論。
3)服務端主動-發送通知(通知拉取):
這是在線消息發送機制之一,簡單理解為服務端給客戶端發送一個通知,通知包含時間戳等可作為排序索引的內容,客戶端收到通知后,依據自身數據,對比通知內時間戳,發起拉取消息的流程。
這種場景適用于較多消息傳遞:比如某人有很多大規模的群,每個群內都有很多成員正在激烈討論。通過通知拉取機制,可以有效的減少客戶端服務端網絡交互次數,并且對多條消息進行打包,提升有效數據載荷。既能保證時效,又能保證性能。
客戶端服務端下行段消息交互示意圖:
4、客戶端與服務端消息交互具體實現
正如上節所言,我們將消息的交互流程進行了拆分:即拆分出上下行。
4.1 上行
在上行過程保證發送消息順序,為了保證消息有序, 最好的方式是按照 userId 區分,然后使用時間戳排序。那么分布式部署情況下,將用戶歸屬到固定的業務服務器上(PS:指的是同一賬號的不同端固定連接到相同的業務服務器上),會使得上行排序變得更容易。同時歸屬到同一個服務器,在多端維護時也更容易。
客戶端連接過程:
- 1)客戶端通過 APP server ,獲取到連接使用的 token;
- 2)客戶端使用 token 通過導航服務,獲取具體連接的 IM 接入服務器(CMP),導航服務通過 userId 計算接入服務器,然后下發,使得某一客戶端可以連接在同一臺接入服務器(CMP)。
示意圖如下:
小結一下就是:客戶端發出消息后,通過接入服務,按照 userId 投遞到指定消息服務器,生成消息 Id, 依據最后一條消息時間,確認更新當前消息的時間戳(如果存在相同時間戳則后延)。然后將時間戳,以及消息 Id,通過 Ack 返回給客戶端 ; 然后對上行消息使用 userId + 時間戳進行緩存以及持久化存儲,后續業務操作均使用此時間戳。
以上業務流程我們稱為上行流程,上行過程存儲的消息為發件箱消息。
PS:關于消息ID,需要補充說明一下:
我們采用全局唯一的消息 ID 生成策略。保證消息可通過 ID 進行識別,排重。消息ID的結構如下圖所示。
如何實現分布式場景下唯一 ID 生成,具體請閱讀:《IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略》。
4.2 下行
消息節點在處理完上行流程后,消息按照目標用戶投遞到所在消息節點,進入下行流程。
下行過程,按照目標 userId 以及本消息在上行過程中生成的時間戳,計算是否需要更新時間戳(正向)。
如果需要更新則對時間戳進行加法操作,直到當前用戶時間戳不重復。
如此處理后,目標用戶的存儲以及客戶端接收到消息后的排重可以做到一致,并且可以做到同一個會話內的時間戳是有序的。從而保證同一個接收用戶的消息不會出現亂序。
至此:我們已經介紹完了消息的下行交互過程,消息下行過程中的具體實現方式并不簡單,以下將詳細展開。
1)直發消息:
即服務端主動發送(給目標客戶端)的消息:
- 1)客戶端 SDK 依據本地存儲的最新消息時間戳判斷,用來做排序等邏輯;
- 2)對同一個用戶直發消息1條,其他轉通知。通知拉取時候客戶端選擇本地最新一條消息時間戳作為開始拉取時間;
- 3)在消息發送過程中,如果上一條消息發送流程未結束,下一條消息則不用直發(s_msg),而是用通知(s_ntf)。
直發邏輯示意圖:
2)通知拉取:
即服務端主動發送通知(給目標客戶端):
- 1)服務端在通知體中攜帶當前消息時間戳。投遞給客戶端;
- 2)客戶端收到通知后,比對本地消息時間戳,選擇是否發拉取消息信令;
- 3)服務端收到拉取消息信令后,以信令攜帶的時間戳為開始,查詢出消息列表(200 條或者 5M),并給客戶端應答;
- 4)客戶端收到后,給服務端 ack,服務端維護狀態;
- 5)客戶端拉取消息時使用的時間戳,是客戶端本地最新一條消息的時間戳。
示意圖:
在上圖中,3-7 步可能需要循環多次,有以下考慮:
- a、客戶端一次收到的消息過多,應答體積過于龐大,傳輸過程對網絡質量要求更高, 因此按照數量以及消息體積分批次進行;
- b、一次拉取到的消息過多,客戶端處理會占用大量資源,可能會有卡頓等,體驗較差。
3)服務端直發消息與通知拉取切換邏輯:
主要涉及到的是狀態機的更新。
下面示意圖集成直發消息與通知拉取過程針對狀態機的更新:
至此,消息收發的整個核心流程介紹完畢,余下的內容將介紹多端在線的消息同步處理。
5、多端在線的消息同步
多端按照消息的上下行兩個階段,同樣區分為發送方多端同步以及接收方多端同步。
5.1 發送方多端同步
在前面客戶端連接 IM 服務過程中(見本文 4.1節),我們已經將同一個用戶的多個客戶端匯聚在了同一臺服務,那么維護一個 userId 的多端就會變得很簡單。
具體邏輯是:
- 1)用戶多個終端鏈接成功后,發送一條消息,這個消息到達 CMP(IM 接入服務) 后,CMP 做基礎檢查,然后獲此用戶的其他終端連接;
- 2)服務把客戶端上行的消息,封裝為服務端下行消息,直接投遞給用戶的其他客戶端。這樣完成了發送方的多端抄送,然后將這條消息投遞到 IM 服務。進入正常發送投遞流程。
針對上面的第2)點,發送方的多端同步沒有經過 IM Server,這么做的好處是:
- 1)比較快速;
- 2)經過越少的服務節點,出問題的幾率越小。
5.2 接收方多端同步
具體邏輯是:
- 1)IM 服務收到消息后,先判斷接收方的投遞范圍,這個范圍指的是接收方用戶的哪些的終端要接收消息;
- 2)IM 服務將范圍以及當前消息,發送到 CMP,CMP 依據范圍,匹配接收方的終端,然后投遞消息。
接收方多端消息同步范圍的應用場景,一般都是針對所有終端。
但有一些特殊業務:比如我在 A 客戶端上,控制另外某個端的狀態,可能需要一些命令消息, 這時候需要這個作用范圍,針對性的投遞消息。
到此,我們分享完了有關 IM 消息核心處理流程,通過層層拆解邏輯,提供了可靠的消息投遞機制。
附錄:更多IM架構設計的文章
《淺談IM系統的架構設計》
《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》
《一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)》
《一套原創分布式即時通訊(IM)系統理論架構方案》
《從零到卓越:京東客服即時通訊系統的技術架構演進歷程》
《蘑菇街即時通訊/IM服務器開發之架構選擇》
《騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT》
《微信后臺基于時間序的海量數據冷熱分級架構設計實踐》
《微信技術總監談架構:微信之道——大道至簡(演講全文)》
《如何解讀《微信技術總監談架構:微信之道——大道至簡》》
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)》
《17年的實踐:騰訊海量產品的技術方法論》
《移動端IM中大規模群消息的推送如何保證效率、實時性?》
《現代IM系統中聊天消息的同步和存儲方案探討》
《IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》
《IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議》
《IM開發基礎知識補課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《WhatsApp技術實踐分享:32人工程團隊創造的技術神話》
《微信朋友圈千億訪問量背后的技術挑戰和實踐總結》
《王者榮耀2億用戶量的背后:產品定位、技術架構、網絡方案等》
《IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?》
《騰訊資深架構師干貨總結:一文讀懂大型分布式系統設計的方方面面》
《以微博類應用場景為例,總結海量社交系統的架構設計步驟》
《快速理解高性能HTTP服務端的負載均衡技術原理》
《子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐》
《知乎技術分享:從單機到2000萬QPS并發的Redis高性能緩存實踐之路》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》
《微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)》
《微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)》
《新手入門:零基礎理解大型分布式架構的演進歷史、技術原理、最佳實踐》
《一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐》
《阿里技術分享:深度揭秘阿里數據庫技術方案的10年變遷史》
《阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路》
《社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等》
《社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進》
《社交軟件紅包技術解密(三):微信搖一搖紅包雨背后的技術細節》
《社交軟件紅包技術解密(四):微信紅包系統是如何應對高并發的》
《社交軟件紅包技術解密(五):微信紅包系統是如何實現高可用性的》
《社交軟件紅包技術解密(六):微信紅包系統的存儲層架構演進實踐》
《社交軟件紅包技術解密(七):支付寶紅包的海量高并發技術實踐》
《社交軟件紅包技術解密(八):全面解密微博紅包技術方案》
《社交軟件紅包技術解密(九):談談手Q紅包的功能邏輯、容災、運維、架構等》
《社交軟件紅包技術解密(十):手Q客戶端針對2020年春節紅包的技術實踐》
《社交軟件紅包技術解密(十一):解密微信紅包隨機算法(含代碼實現)》
《即時通訊新手入門:一文讀懂什么是Nginx?它能否實現IM的負載均衡?》
《即時通訊新手入門:快速理解RPC技術——基本概念、原理和用途》
《多維度對比5款主流分布式MQ消息隊列,媽媽再也不擔心我的技術選型了》
《從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路》
《從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結》
《從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐》
《IM開發基礎知識補課(六):數據庫用NoSQL還是SQL?讀這篇就夠了!》
《瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)》
《阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處》
《微信后臺基于時間序的新一代海量數據存儲架構的設計實踐》
《IM開發基礎知識補課(九):想開發IM集群?先搞懂什么是RPC!》
《阿里技術分享:電商IM消息平臺,在群聊、直播場景下的技術實踐》
《一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等》
《一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等》
《從新手到專家:如何設計一套億級消息量的分布式IM系統》
《企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等》
《融云技術分享:全面揭秘億級IM消息的可靠投遞機制》
>> 更多同類文章 ……
本文已同步發布于“即時通訊技術圈”公眾號。

▲ 本文在公眾號上的鏈接是:點此進入。同步發布鏈接是:http://www.52im.net/thread-3638-1-1.html