隨著即時通信(Instant Messaging)場景和客戶端種類的不斷豐富,多端通信已經成為普遍趨勢。本文為您介紹一種使用云數據庫 Tair(兼容 Redis)在IM場景中實現多端同步的方案。
消息存儲模型
通常,IM系統的核心架構分為三個部分:消息管理模塊、消息同步模塊、通知模塊。這三個模塊的作用如下:
消息管理模塊主要負責接收和存儲消息。
消息同步模塊主要負責存儲和推送下行消息數據及其狀態。
通知模塊主要負責維護第三方通道和通知功能。
消息管理模塊的核心是消息存儲模型,存儲模型的選型直接影響著消息同步模塊的實現。消息、會話、會話與消息組織關系的實現方式在業界各主流IM系統中都不盡相同,但無外乎兩種形式:寫擴散讀聚合、讀擴散寫聚合。讀、寫擴散是消息在群組會話中的存儲形式,其詳細說明如下。
在讀擴散場景中,消息歸屬于會話,相當于數據庫中存儲著一張conversation_message表,其中包含該會話產生的所有消息。這種存儲形式的好處是消息入庫效率高,只保存會話與消息的綁定關系即可。
在寫擴散場景中,會話產生的消息投遞到message_inbox表中,該表類似于個人郵件的收件箱,其中保存著個人的所有會話,會話中的消息按其產生的時間順序排列。這種存儲形式的好處是能實現靈活的消息狀態管理,會話中的每條消息在面向不同的接收者時可以呈現出不同的狀態。
如果采用讀擴散的方式,在大并發修改數據的場景下,數據一致性處理效率和數據變更效率會成為系統性能瓶頸。因此,下文介紹的案例采用寫擴散的方式實現消息存儲模型,以更高的存儲成本支持更高的更新性能。
消息同步模塊
多端同步的核心問題在于多端數據的一致性,IM系統需要記錄消息的順序和每個端的同步點,從而實現消息的最終一致性。
既然采用寫擴散的方式來記錄消息,系統需要:
為每個用戶創建一個message_inbox,用于儲存該用戶的消息。
為每一條消息創建一個自增的sync_id,用于記錄消息的順序。
記錄用戶在每個客戶端上的同步ID。
通過對用戶在各客戶端上的數據進行對比和同步,就可以實現多端數據同步,詳細的實現方式如下。
提煉數據結構。從IM系統中的各類事件中提煉出統一的消息數據結構,這些事件包括新消息、已讀消息、增刪會話信息等。消息數據結構示例如下:
struct message { int type; // 業務類型 string data; // 業務數據 }
進行存儲產品選型。選型依據主要有以下兩點:
系統需要為message_inbox中的每條消息分配一個自增的sync_id,所以用于存儲消息數據的產品需要能實現原子遞增隊列。
完整的消息需要在IM系統的消息管理模塊中保存到持久化存儲(例如PolarDB)中,而message_inbox數據則無需持久化存儲,只需存儲一段時間(例如一周)即可,所以對存儲的容量要求并不高。
支持計數器功能和Sorted Sets結構的云數據庫 Tair(兼容 Redis)正好能滿足上述要求。
通過云數據庫 Tair(兼容 Redis)的Hash結構來存儲每個用戶在客戶端上的同步ID。
場景案例
下圖基于一個案例展示了多端同步的詳細實現方式。
圖中的Bob為虛擬的用戶名。
新消息入庫以后,推送消息邏輯被觸發,系統根據用戶名獲取到所有客戶端設備的當前點位,然后從消息隊列中獲取歷史點位到最新點位間的所有消息,再將其推送到客戶端設備。推送完成后,更新設備的當前點位信息。關鍵步驟的示例代碼如下。
新消息入庫:
sync_id = INCR bob ZADD bob $sync_id message:{type:new_message, data:"{msgid:991,cid:123,text:"hello"}"}
獲取消息范圍:
ZRangeByScore bob 100103 100310
獲取客戶端設備的點位:
HGETALL bob
加入或更新客戶端設備信息:
HSET bob dev_1001 100103 HSET bob dev_1002 100202
總結
IM通信已經成為互聯網環境中最常見通信方式之一,借助云數據庫 Tair(兼容 Redis)豐富的數據結構,您可以構建出高可用的IM系統。不僅是本文提到的消息同步模塊,IM系統的消息存儲模塊也可以使用Redis進行加速,最終構建出支持大規模訪問的可靠IM系統。