摘要:本章主要會介紹基於 TableStore 的現代 IM 消息系統的架構設計,在詳細介紹架構設計之前,會先介紹一種 Timeline 邏輯模型,來抽象和簡化對 IM 消息同步和存儲模型的理解。基於 Timeline 模型,在實現消息同步和存儲時還會有各方面的技術權衡,例如如何對消息同步常見的讀擴散和寫擴散兩種模型進行對比和選擇,以及針對 Timeline 模型的特徵如何來選擇底層數據庫。

作者:木洛,阿里雲高級技術專家
來源:https://zhuanlan.zhihu.com/p/31143916


前言

IM 全稱是“Instant Messaging”,中文名是即時通訊。在這個高度信息化的移動互聯網時代,生活中 IM 類產品已經成爲必備品,比較有名的如釘釘、微信、QQ 等以 IM 爲核心功能的產品。當然目前微信已經成長爲一個生態型產品,但其核心功能還是 IM。還有一些非以 IM 系統爲核心的應用,最典型的如一些在線遊戲、社交應用,IM 也是其重要的功能模塊。可以說,帶有社交屬性的應用,IM 功能一定是必不可少的。

IM 系統在互聯網初期即存在,其基礎技術架構在這十幾年的發展中更新迭代多次,從早期的 CS、P2P 架構,到現在後臺已經演變爲一個複雜的分佈式系統,涉及移動端、網絡、安全和存儲等技術的方方面面。其支撐的規模也從早期的少量日活,到現在微信這個巨頭最新公佈的達到 9 億日活的體量。

IM 系統中最核心的部分是消息系統,消息系統中最核心的功能是消息的同步和存儲:

  • 消息的同步:將消息完整、快速地從發送方傳遞到接收方,就是消息的同步。消息同步系統最重要的衡量指標就是消息傳遞的實時性、完整性以及能支撐的消息規模。從功能上來說,一般至少要支持在線和離線推送,高級的 IM 系統還支持“多端同步”。


  • 消息的存儲:消息存儲即消息的持久化保存,這裏不是指消息在客戶端本地的保存,而是指雲端的保存,功能上對應的就是“消息漫遊”。“消息漫遊”的好處是可以實現賬號在任意端登陸查看所有歷史消息,這也是高級 IM 系統特有的功能之一。


本文內容主要涉及 IM 系統中的消息系統架構,會介紹一種基於 TableStore 構建的消息同步以及存儲系統的架構實現,能夠支持消息系統中的高級特性“多端同步”以及“消息漫遊”。在性能和規模上,能夠做到全量消息雲端存儲,百萬 TPS 以及毫秒級延遲的消息同步能力。

架構設計

本章主要會介紹基於 TableStore 的現代 IM 消息系統的架構設計,在詳細介紹架構設計之前,會先介紹一種 Timeline 邏輯模型,來抽象和簡化對 IM 消息同步和存儲模型的理解。理解了 Timeline 模型後,會介紹如何基於此模型對消息的同步以及存儲進行建模。基於 Timeline 模型,在實現消息同步和存儲時還會有各方面的技術權衡,例如如何對消息同步常見的讀擴散和寫擴散兩種模型進行對比和選擇,以及針對 Timeline 模型的特徵如何來選擇底層數據庫。

傳統架構 vs 現代架構


現代 IM 系統中消息推送和存儲架構的實現


上圖是消息系統傳統架構與現代架構的簡單對比。

傳統架構下,消息是先同步後存儲。對於在線的用戶,消息會直接實時同步到在線的接收方,消息同步成功後,並不會進行持久化。而對於離線的用戶或者消息無法實時同步成功時,消息會持久化到離線庫,當接收方重新連接後,會從離線庫拉取所有未讀消息。當離線庫中的消息成功同步到接收方後,消息會從離線庫中刪除。傳統的消息系統,服務端的主要工作是維護髮送方和接收方的連接狀態,並提供在線消息同步和離線消息緩存的能力,保證消息一定能夠從發送方傳遞到接收方。服務端不會對消息進行持久化,所以也無法支持消息漫遊。

現代架構下,消息是先存儲後同步。先存儲後同步的好處是,如果接收方確認接收到了消息,那這條消息一定是已經在雲端保存了。並且消息會有兩個庫來保存,一個是消息存儲庫,用於全量保存所有會話的消息,主要用於支持消息漫遊。另一個是消息同步庫,主要用於接收方的多端同步。消息從發送方發出後,經過服務端轉發,服務端會先將消息保存到消息存儲庫,後保存到消息同步庫。完成消息的持久化保存後,對於在線的接收方,會直接選擇在線推送。但在線推送並不是一個必須路徑,只是一個更優的消息傳遞路徑。對於在線推送失敗或者離線的接收方,會有另外一個統一的消息同步方式。接收方會主動的向服務端拉取所有未同步消息,但接收方何時來同步以及會在哪些端來同步消息對服務端來說是未知的,所以要求服務端必須保存所有需要同步到接收方的消息,這是消息同步庫的主要作用。對於新的同步設備,會有消息漫遊的需求,這是消息存儲庫的主要作用,在消息存儲庫中,可以拉取任意會話的全量歷史消息。

以上是傳統架構和現代架構的一個簡單的對比,現代架構上整個消息的同步和存儲流程,並沒有變複雜太多,但是其能實現多端同步以及消息漫遊。現代架構中最核心的就是兩個消息庫“消息同步庫”和“消息存儲庫”,是消息同步和存儲最核心的基礎。而本文接下來的部分,都是圍繞這兩個庫的設計和實現來展開。

Timeline 模型

在分析“消息同步庫”和“消息存儲庫”的設計和實現之前,在本章會先介紹一個邏輯模型——Timeline。Timeline 模型會幫助我們簡化對消息同步和存儲模型的理解,而消息庫的設計和實現也是圍繞 Timeline 的特性和需求來展開。


現代 IM 系統中消息推送和存儲架構的實現



如圖是 Timeline 模型的一個抽象表述,Timeline 可以簡單理解爲是一個消息隊列,但這個消息隊列有如下特性:

  • 每個消息擁有一個順序 ID(SeqId),在隊列後面的消息的 SeqId 一定比前面的消息的 SeqId 大,也就是保證 SeqId 一定是增長的,但是不要求嚴格遞增。
  • 新的消息永遠在尾部添加,保證新的消息的 SeqId 永遠比已經存在隊列中的消息都大。
  • 可根據 SeqId 隨機定位到具體的某條消息進行讀取,也可以任意讀取某個給定範圍內的所有消息。


有了這些特性後,消息的同步可以拿 Timeline 來很簡單的實現。圖中的例子中,消息發送方是 A,消息接收方是 B,同時 B 存在多個接收端,分別是 B1、B2 和 B3。A 向 B 發送消息,消息需要同步到 B 的多個端,待同步的消息通過一個 Timeline 來進行交換。A 向 B 發送的所有消息,都會保存在這個 Timeline 中,B 的每個接收端都是獨立的從這個 Timeline 中拉取消息。每個接收端同步完畢後,都會在本地記錄下最新同步到的消息的 SeqId,即最新的一個位點,作爲下次消息同步的起始位點。服務端不會保存各個端的同步狀態,各個端均可以在任意時間從任意點開始拉取消息。

消息漫遊也是基於 Timeline,和消息同步唯一的區別是,消息漫遊要求服務端能夠對 Timeline 內的所有數據進行持久化。

基於 Timeline,從邏輯模型上能夠很簡單的理解在服務端如何去實現消息同步和存儲,並支持多端同步和消息漫遊這些高級功能。落地到實現的難點主要在如何將邏輯模型映射到物理模型,Timeline 的實現對數據庫會有哪些要求?我們應該選擇何種數據庫去實現?這些是接下來會討論到的問題。

消息存儲模型


現代 IM 系統中消息推送和存儲架構的實現



如圖是基於Timeline的消息存儲模型,消息存儲要求每個會話都對應一個獨立的 Timeline。如圖例子所示,A 與 B/C/D/E/F 均發生了會話,每個會話對應一個獨立的 Timeline,每個 Timeline 內存有這個會話中的所有消息,服務端會對每個 Timeline 進行持久化。服務端能夠對所有會話 Timeline 中的全量消息進行持久化,也就擁有了消息漫遊的能力。

消息同步模型

消息同步模型會比消息存儲模型稍複雜一些,消息的同步一般有讀擴散和寫擴散兩種不同的方式,分別對應不同的 Timeline 物理模型。


現代 IM 系統中消息推送和存儲架構的實現



如圖是讀擴散和寫擴散兩種不同同步模式下對應的不同的 Timeline 模型,按圖中的示例,A 作爲消息接收者,其與 B/C/D/E/F 發生了會話,每個會話中的新的消息都需要同步到 A 的某個端,看下讀擴散和寫擴散兩種模式下消息如何做同步。

  • 讀擴散:消息存儲模型中,每個會話的 Timeline 中保存了這個會話的全量消息。讀擴散的消息同步模式下,每個會話中產生的新的消息,只需要寫一次到其用於存儲的 Timeline 中,接收端從這個 Timeline 中拉取新的消息。優點是消息只需要寫一次,相比寫擴散的模式,能夠大大降低消息寫入次數,特別是在羣消息這種場景下。但其缺點也比較明顯,接收端去同步消息的邏輯會相對複雜和低效。接收端需要對每個會話都拉取一次才能獲取全部消息,讀被大大的放大,並且會產生很多無效的讀,因爲並不是每個會話都會有新消息產生。


  • 寫擴散:寫擴散的消息同步模式,需要有一個額外的 Timeline 來專門用於消息同步,通常是每個接收端都會擁有一個獨立的同步 Timeline,用於存放需要向這個接收端同步的所有消息。每個會話中的消息,會產生多次寫,除了寫入用於消息存儲的會話 Timeline,還需要寫入需要同步到的接收端的同步 Timeline。在個人與個人的會話中,消息會被額外寫兩次,除了寫入這個會話的存儲 Timeline,還需要寫入參與這個會話的兩個接收者的同步 Timeline。而在羣這個場景下,寫入會被更加的放大,如果這個羣擁有 N 個參與者,那每條消息都需要額外的寫 N 次。寫擴散同步模式的優點是,在接收端消息同步邏輯會非常簡單,只需要從其同步 Timeline 中讀取一次即可,大大降低了消息同步所需的讀的壓力。其缺點就是消息寫入會被放大,特別是針對羣這種場景。


在 IM 這種應用場景下,通常會選擇寫擴散這種消息同步模式。IM 場景下,一條消息只會產生一次,但是會被讀取多次,是典型的讀多寫少的場景,消息的讀寫比例大概是 10:1。若使用讀擴散同步模式,整個系統的讀寫比例會被放大到 100:1。一個優化的好的系統,必須從設計上去平衡這種讀寫壓力,避免讀或寫任意一維觸碰到天花板。所以 IM 系統這類場景下,通常會應用寫擴散這種同步模式,來平衡讀和寫,將 100:1 的讀寫比例平衡到 30:30。當然寫擴散這種同步模式,還需要處理一些極端場景,例如萬人大羣。針對這種極端寫擴散的場景,會退化到使用讀擴散。一個簡單的 IM 系統,通常會在產品層面限制這種大羣的存在,而對於一個高級的 IM 系統,會採用讀寫擴散混合的同步模式,來滿足這類產品的需求。

消息庫設計

基於 Timeline 模型,以及 Timeline 模型在消息存儲和消息同步的應用,我們看下消息同步庫和消息存儲庫的設計。


現代 IM 系統中消息推送和存儲架構的實現



如圖是基於 Timeline 的消息庫設計。

  • 消息同步庫:消息同步庫用於存儲所有用於消息同步的 Timeline,每個 Timeline 對應一個接收端,主要用作寫擴散模式的消息同步。這個庫不需要永久保留所有需要同步的消息,因爲消息在同步到所有端後其生命週期就可以結束,就可以被回收。但是如前面所介紹的,一個實現簡單的多端同步消息系統,在服務端不會保存有所有端的同步狀態,而是依賴端自己主動來做同步。所以服務端不知道消息何時可以回收,通常的做法是爲這個庫裏的消息設定一個固定的生命週期,例如一週或者一個月,生命週期結束可被淘汰。


  • 消息存儲庫:消息存儲庫用於存儲所有會話的 Timeline,每個 Timeline 包含了一個會話中的所有消息。這個庫主要用於消息漫遊時拉取某個會話的所有歷史消息,也用於讀擴散模式的消息同步。


消息同步庫和消息存儲庫,對數據庫有不同的要求,如何對數據庫做選型,在下面會討論。

數據庫選型

消息系統最核心的兩個庫是消息同步庫和消息存儲庫,兩個庫對數據庫有不同的要求:


現代 IM 系統中消息推送和存儲架構的實現



總結下來,對數據庫的要求有如下幾點:

1. 表結構設計能夠滿足 Timeline 模型的功能要求:不要求關係模型,能夠實現隊列模型,並能夠支持生成自增的 SeqId。

2. 能夠支持高併發寫和範圍讀,規模在十萬級 TPS。

3. 能夠保存海量數據,百 TB 級。

4. 能夠爲數據定義生命週期。

阿里雲表格存儲(TableStore)是基於 LSM 存儲引擎的分佈式 NoSQL 數據庫,支持百萬 TPS 高併發讀寫,PB 級數據存儲,數據支持 TTL,能夠很好的滿足以上需求,並且支持自增列,能夠非常完美的設計和實現 Timeline 的物理模型。

架構實現

本章會以一段非常精簡的代碼,來展示如何基於 TableStore 實現 Timeline 模型,並基於 Timeline 模型進行消息存儲和推送。本文中給出的代碼,主要目的是爲了演示如何能夠實現一個精簡 Timeline 的最基本功能。馬上我們會推出一個完整的 Timeline Library,來將基於 Timeline 進行消息存儲和推送的代碼的開發變得無比簡單。

所有示例代碼基於如下 SDK 版本:


現代 IM 系統中消息推送和存儲架構的實現



表結構設計


現代 IM 系統中消息推送和存儲架構的實現


現代 IM 系統中消息推送和存儲架構的實現



以上是創建 Timeline 表的示例代碼,總共需要創建兩張表,一張表作爲消息同步庫,名稱爲“PushTable”,另一張表作爲消息存儲庫,名稱爲“StoreTable”。


推送和存儲實現

現代 IM 系統中消息推送和存儲架構的實現


以上是模擬一個羣內消息同步和存儲的示例代碼。羣名稱爲『TableStore(釘釘號:11789671)』,羣內成員有『A, B, C, D, E』。羣內新的消息,需要先存儲到羣的存儲 Timeline(Timeline ID 爲羣名稱),之後需要以寫擴散的模式推送到羣內每個成員的同步 Timeline(以羣成員名稱作爲 Timeline ID)。

現代 IM 系統中消息推送和存儲架構的實現

現代 IM 系統中消息推送和存儲架構的實現


以上是拉取羣內歷史消息以及某個羣成員進行消息同步的示例代碼,主要邏輯在 syncMessages 函數內。示例代碼中,拉取消息都是從 seq_id 爲 0 開始,0 爲 TableStore 自增列中最小值,所以代表了從最小的一個位點開始拉取消息,即拉取全量消息。

後記

這篇文章主要介紹了現代IM系統中消息推送和存儲架構的實現,基於邏輯的 Timeline 模型,我們可以很清晰明瞭的理解整個消息推送和存儲的架構。基於 TableStore,可以非常簡單的實現 Timeline 模型,其中自增列功能,完美的匹配了 Timeline 模型中所需要的最關鍵的 SeqId 自增。

TableStore(表格存儲)是阿里雲自主研發的專業級分佈式 NoSQL 數據庫,是基於共享存儲的高性能、低成本、易擴展、全託管的半結構化數據存儲平臺,支撐互聯網和物聯網數據的高效計算與分析。IM 系統的消息推送和存儲場景,是 TableStore 在社交領域的重要應用之一。

基於 Timeline 的消息存儲和推送模型,將不光應用在 IM 消息系統中,還可應用在例如 Feeds 流、實時消息同步、直播彈幕等場景。

34張架構史上最全技術知識圖譜

程序員專屬手機壁紙來了。。。

相關文章