融雲技術分享:全面揭祕億級IM訊息的可靠投遞機制

語言: CN / TW / HK

本文由融雲技術團隊原創分享,原題“IM 訊息同步機制全面解析”,為使文章更好理解,對內容進行了重新歸納和細節修訂。

1、內容概述

即時通訊(IM)系統最基礎、最重要的是訊息的及時性與準確性,及時體現在延遲,準確則具體表現為不丟、不重、不亂序。

綜合考慮業務場景、系統複雜度、網路流量、終端能耗等,我們的億級分散式IM訊息系統精心設計了訊息收發機制,並不斷打磨優化,形成了現在的訊息可靠投遞機制。

整體思路就是:

  • 1)客戶端、服務端共同配合,互相補充;
  • 2)採用多重機制,從不同層面保障;
  • 3)拆分上下行,分別處理。

本文根據融雲億級IM訊息系統的技術實踐,總結了分散式IM訊息的可靠投遞機制,希望能為你的IM開發和知識學習起到拋磚引玉的作用。

2、客戶端與服務端訊息互動整體原理

2.1 概述

一個完整的IM訊息互動邏輯,通常會為兩段:

  • 1)訊息上行段:即由訊息傳送者通過IM實時通道傳送給服務端;
  • 2)訊息下行段:由服務端按照一定的策略送達給最終的訊息接收人。

2.2 訊息上行段

訊息上行段主要就是依賴IM的實時通道將訊息傳遞給服務端。

這個階段的訊息可靠投遞,需要從協議層進行保證,協議層需要提供可靠、有序的雙向位元組流傳輸,我們是通過自研的通訊協議 RMTP(即 RongCloud Message Transfer Protocol)實現的。

客戶端與服務端之間使用長連線,基於 RMTP 協議傳輸資料。

RMTP協議互動示意圖:

如上圖所示,協議層通過 QoS、 ACK 等機制,保證IM訊息上行段資料傳輸的可靠性。

2.3 訊息下行段

經過總結,訊息下行段主要有三種行為。

1)客戶端主動拉取訊息,主動拉取有兩個觸發方式:

  • ① 拉取離線訊息:與 IM 服務新建立連線成功,用於獲取不線上的這段時間未收到的訊息;
  • ② 定時拉取訊息:在客戶端最後收到訊息後啟動定時器,比如 3-5 分鐘執行一次。主要有兩個目的,一個是用於防止因網路、中間裝置等不確定因素引起的通知送達失敗,服務端客戶端狀態不一致,一個是可通過本次請求,對業務層做狀態機保活。

2)服務端主動-傳送訊息(直髮訊息):

這是線上訊息傳送機制之一,簡單理解為服務端將訊息內容直接傳送給客戶端,適用於訊息頻率較低,並且持續互動,比如二人或者群內的正常交流討論。

3)服務端主動-傳送通知(通知拉取):

這是線上訊息傳送機制之一,簡單理解為服務端給客戶端傳送一個通知,通知包含時間戳等可作為排序索引的內容,客戶端收到通知後,依據自身資料,對比通知內時間戳,發起拉取訊息的流程。

這種場景適用於較多訊息傳遞:比如某人有很多大規模的群,每個群內都有很多成員正在激烈討論。通過通知拉取機制,可以有效的減少客戶端服務端網路互動次數,並且對多條訊息進行打包,提升有效資料載荷。既能保證時效,又能保證效能。

客戶端服務端下行段訊息互動示意圖:

3、客戶端與服務端訊息互動具體實現

正如上節所言,我們將訊息的互動流程進行了拆分:即拆分出上下行。

3.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生成策略》。

3.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)服務端直髮訊息與通知拉取切換邏輯:

主要涉及到的是狀態機的更新。

下面示意圖整合直髮訊息與通知拉取過程針對狀態機的更新:

至此,訊息收發的整個核心流程介紹完畢,餘下的內容將介紹多端線上的訊息同步處理。

4、多端線上的訊息同步

多端按照訊息的上下行兩個階段,同樣區分為傳送方多端同步以及接收方多端同步。

4.1 傳送方多端同步

在前面客戶端連線 IM 服務過程中(見本文 4.1節),我們已經將同一個使用者的多個客戶端匯聚在了同一臺服務,那麼維護一個 userId 的多端就會變得很簡單。

具體邏輯是:

  • 1)使用者多個終端連結成功後,傳送一條訊息,這個訊息到達 CMP(IM 接入服務) 後,CMP 做基礎檢查,然後獲此使用者的其他終端連線;
  • 2)服務把客戶端上行的訊息,封裝為服務端下行訊息,直接投遞給使用者的其他客戶端。這樣完成了傳送方的多端抄送,然後將這條訊息投遞到 IM 服務。進入正常傳送投遞流程。

針對上面的第2)點,傳送方的多端同步沒有經過 IM Server,這麼做的好處是:

  • 1)比較快速;
  • 2)經過越少的服務節點,出問題的機率越小。

4.2 接收方多端同步

具體邏輯是:

  • 1)IM 服務收到訊息後,先判斷接收方的投遞範圍,這個範圍指的是接收方使用者的哪些的終端要接收訊息;
  • 2)IM 服務將範圍以及當前訊息,傳送到 CMP,CMP 依據範圍,匹配接收方的終端,然後投遞訊息。

接收方多端訊息同步範圍的應用場景,一般都是針對所有終端。

但有一些特殊業務:比如我在 A 客戶端上,控制另外某個端的狀態,可能需要一些命令訊息, 這時候需要這個作用範圍,針對性的投遞訊息。

到此,我們分享完了有關 IM 訊息核心處理流程,通過層層拆解邏輯,提供了可靠的訊息投遞機制。

5、參考資料

以下是融雲技術團隊分享的其它文章:

本文已同步釋出於“即時通訊技術圈”公眾號。

▲ 本文在公眾號上的連結是:點此進入。同步釋出連結是:http://www.52im.net/thread-3638-1-1.html

「其他文章」