本文由融雲技術團隊原創分享,原題“IM 訊息資料儲存結構設計”,內容有修訂。
1、引言
在如今的移動網際網路時代,IM類產品已是我們生活中不可或缺的組成部分。像微信、釘釘、QQ等是典型的以 IM 為核心功能的社交產品。另外也有一些應用雖然IM功能不是核心,但IM能力也是其整個應用極其重要的組成部分,比如線上遊戲、電商直播等應用。
在IM技術應用場景越來越廣泛的前提下,對即時通訊IM技術的學習和掌握就顯的越來越有必要。
在IM龐大的技術體系中,訊息系統無疑是最核心的,而訊息系統中,最關鍵的部分是訊息的分發和儲存,而離線訊息和歷史訊息又是這個關鍵環節中不可迴避的技術要點。
本文將基於IM訊息系統的技術實踐,分享關於離線訊息和歷史訊息的正確理解,以及具體的技術配合和實踐,希望能為你的離線訊息和歷史訊息技術設計帶來最佳實踐靈感。
學習交流:
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
- 開源IM框架原始碼:https://github.com/JackJiang2...
(本文同步釋出於:http://www.52im.net/thread-38...)
2、相關文章
技術相關文章:
《什麼是IM系統的可靠性?》
《閒魚IM的線上、離線聊天資料同步機制優化實踐》
《閒魚億級IM訊息系統的可靠投遞優化實踐》
《一套億級使用者的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等》
《IM訊息送達保證機制實現(二):保證離線訊息的可靠投遞》
《我是如何解決大量離線訊息導致客戶端卡頓的》
融雲技術團隊分享的其它文章:
《融雲安卓端IM產品的網路鏈路保活技術實踐》
《全面揭祕億級IM訊息的可靠投遞機制》
《解密融雲IM產品的聊天訊息ID生成策略》
《萬人群聊訊息投遞方案的思考和實踐》
《基於WebRTC的實時音視訊首幀顯示時間優化實踐》
《融雲IM技術分享:萬人群聊訊息投遞方案的思考和實踐》
3、IM訊息投遞的一般做法
在通常的IM訊息系統中,對於實時訊息、離線訊息、歷史訊息大概都是下面這樣的技術思路。
對於線上使用者:訊息會直接實時傳送到線上的接收方,訊息傳送完成後,伺服器端並不會對訊息進行落地儲存。
而對於離線的使用者:伺服器端會將訊息存入到離線庫,當使用者登入後,從離線庫中將離線訊息拉走,然後伺服器端將離線訊息刪除。
這樣實現的缺點就是訊息不持久化,導致訊息無法支援訊息漫遊,降低了訊息的可靠性。
(PS:實際上,這其實也不能算是缺點,因為一些場景下儲存歷史訊息並不是必須的,所謂的訊息漫遊能力也不是必備的,比如微信。)
而在我們設計的訊息系統中,伺服器只要接收到了傳送方發上來的訊息,在轉發給接收方的同時也會在離線資料庫及歷史訊息庫中進行訊息的落地儲存,而歷史訊息的落地也就能支援訊息漫遊等相關功能了。
4、什麼是離線訊息和歷史訊息?
關於離線訊息和歷史訊息,在技術上,我們是這樣定義。
1)離線訊息:
離線訊息就是使用者(即接收方)在離線過程中收到的訊息,這些訊息大多是使用者比較關心的訊息,具有一定的時效性。
以我們的系統經驗來說,我們的離線訊息預設只儲存最近七天的訊息。
使用者(即接收方)在下次登入後會全量獲取這些離線訊息,然後在客戶端根據聊天會話進行離線訊息的UI展示(比如顯示一個未讀訊息氣泡等)。
(PS:使用者離線的可能性在技術上其實是由很多種情況組成的,比如對方不線上、對方網路斷掉了、對方手機崩潰了、伺服器傳送時出錯了等等,嚴格來講——只要無法實時傳送成的訊息,都算“離線訊息”。)
2)歷史訊息:
歷史訊息儲存了使用者所有的聊天訊息,這些訊息包括髮出的訊息以及接收到的訊息。
在客戶端獲取歷史訊息時,通常是按照會話進行分頁獲取的。
以我們的系統經驗來說,歷史訊息的儲存時間我們設計預設為半年,當然這個時間可以按實際的產品運營規則來定,沒有硬性規定。
5、IM訊息的傳送及儲存流程
以下是我們系統整體的訊息傳送及儲存流程:
如上圖所示:當使用者傳送聊天訊息到伺服器端後,首先會進入到訊息系統中,訊息系統會對訊息進行分發以及儲存。
這個過程中:對於線上的接收方,會選擇直接推送訊息。但是遇到接收方不線上或者是訊息推送失敗的情況下,也會有另外的訊息獲取方式,比如接收方會主動向伺服器拉取未收到的訊息。但是接收方何時來伺服器拉取訊息以及從哪裡拉取是未知的,所以訊息存入到離線庫的意義也就在這裡。
訊息系統儲存離線的過程中,為了不影響整個系統的更為平穩,我們使用了MQ訊息佇列進行IO解偶,所以聊天訊息實際上是非同步存入到離線庫中的(通過MQ進行慢IO解偶,這其實也是慣常做法)。
在分發完訊息後:訊息服務會同步一份訊息資料到歷史訊息服務中,歷史訊息服務同樣會對訊息進行落地儲存。
對於新的客戶端裝置:會有同步訊息的需求(所謂的訊息漫遊能力),而這也正是歷史訊息的主要作用。在歷史訊息庫中,客戶端是可以拉取任意會話的全量歷史訊息的。
6、IM離線訊息、歷史訊息在儲存邏輯上的區別
6.1 概述
通過上面的圖中能清晰的看到:
1)離線訊息我們儲存介質選用的是 Redis;
2)歷史訊息我們選用的是 HBase。
對於為什麼選用不同的儲存介質,其實我們考慮的是離線訊息和歷史訊息不同的業務場景和讀寫模式。
下面我們重點介紹一下離線訊息和歷史訊息儲存的區別。
6.2 離線訊息儲存模式——“擴散寫”
離線訊息的儲存模式我們用的是擴散寫。
如上圖所示:每個使用者都有自己單獨的收件箱和發件箱:
1)收件箱存放的是需要向這個接收端同步的所有訊息;
2)發件箱裡存放的是傳送端發出的所有訊息。
以單聊為例:聊天中的兩人會話中,訊息會產生兩次寫,即傳送者的發件箱和接收端的收件箱。
而在群的場景下:寫入會被更加的放大(擴散),如果群裡有 N 個人,那一條群訊息就會被擴散寫 N 次。
小結一下:
1)擴散寫的優點是:接收端的邏輯會非常清晰簡單,只需要從收件箱裡讀取一次即可,大大降低了同步訊息所需的讀的壓力;
2)擴散寫的缺點是:寫入會被成指數地放大,特別是針對群這種場景。
6.3 歷史訊息儲存模式——“擴散讀”
歷史訊息的儲存模式我們用的是擴散讀。
因為歷史訊息中,每個會話都儲存了整個會話的全量訊息。在擴散讀這種模式下,每個會話的訊息只儲存一次。
對比擴散寫模式,擴散讀的優點和缺點如下:
1)優點是:寫入次數大大降低,特別是針對群訊息,只需要存一次即可;
2)缺點是:接收端接收訊息非常的複雜和低效,因為這種模式客戶端想拉取到所有訊息就只能每個會話同步一次,讀就會被放大,而且可能會產生很多次無效的讀,因為有些會話可能根本沒有新訊息。
6.4 小結
在 IM 這種應用場景下,通常會用到擴散寫這種訊息同步模型,一條訊息產生一條,但是可能會被讀多次,是典型的讀多寫少的場景。
一個優化好的IM系統,必須從設計上平衡讀寫壓力,避免讀或者寫任意一個維度達到天花板。
當然擴散寫這種模式也有其弊端,比如萬人群,會導致一條訊息,寫入了一萬次。
綜合來講:我們需要根據自己的業務場景做相應設計選擇,以我們的IM系統為例,就是是根據了離線和歷史訊息的不同場景選擇了寫擴散和讀擴散的組合模式。適合的才是最好的,沒有必要死搬硬套理論。
7、IM客戶端的拉取訊息邏輯
7.1 離線訊息拉取邏輯
對於IM客戶端而言,離線訊息的獲取針對的是自己的整個離線訊息,包括所有的會話(直白了說,就是上線時拉取此次離線過程中的所有未收取的離線訊息)。
離線訊息的獲取是自上而下的方式(按時間序),我們的經驗是一次獲取 200 條(PS:如果離線訊息過多,會分頁多次拉取,拉取1“次”可以理解為拉取1“頁”)。
在客戶端拉取離線訊息的信令中,需要帶上當前客戶端快取的訊息的最大時間戳。
通過上節的圖我們應該知道,離線訊息我們儲存的是一個線性結構(指的是按時間順序),Server 會根據這個時間戳向下查詢離線訊息。當重灌或者新安裝 App 時,客戶端的“當前客戶端快取的訊息的最大時間戳”可以傳 0 上來。
Server 也會快取客戶端拉取到的最後一條訊息的時間戳,然後根據業務場景,客戶端型別等因素來決定從哪裡開始拉取,如果沒有拉取完 Server 會在拉取訊息的應答中帶相應的標記位,告訴客戶端繼續拉取,客戶端迴圈拉取,直到所有離線訊息拉完。
7.2 歷史訊息拉取邏輯
歷史訊息的獲取通常針對的是單一會話。
在拉取過程中,需要向服務端提交兩個引數:
1)對方的 ID(如果是單聊的話就是對方的 UserID,如果是群則是群組ID);
2)當前會話的最前面訊息的時間戳(即當前會話最老一條訊息的時間戳)。
Server據這兩個引數,可以定位到這個客戶端的此會話,然後一次獲取 20 條歷史訊息。
訊息的拉取時序上採用的是自下而上的方式(也就是時間序逆序),即從最後面往前翻。只要有訊息,客戶端可以一直向前翻,手動觸發獲取會話的歷史訊息。
上面的拉取邏輯,在IM介面功能上通常對應的是下拉或點選“載入更多”,比如這樣:
8、本文小結
本文主要分享了IM中有關離線訊息和歷史訊息的正確,主要包括離線訊息和歷史訊息的區別,以及二者在儲存、分發、拉取邏輯方面的最佳踐等。如對文中內容有異議,歡迎留言討論。
9、參考資料
[1] 一套海量線上使用者的移動端IM架構設計實踐分享(含詳細圖文)
[2] 一套原創分散式即時通訊(IM)系統理論架構方案
[3] 從零到卓越:京東客服即時通訊系統的技術架構演進歷程
[4] 一套億級使用者的IM架構技術乾貨(上篇):整體架構、服務拆分等
[5] 閒魚億級IM訊息系統的架構演進之路
[6] 閒魚億級IM訊息系統的可靠投遞優化實踐
[7] 閒魚億級IM訊息系統的及時性優化實踐
[8] 基於實踐:一套百萬訊息量小規模IM系統技術要點總結
[9] IM訊息送達保證機制實現(一):保證線上實時訊息的可靠投遞
[10] 理解IM訊息“可靠性”和“一致性”問題,以及解決方案探討
[11] 零基礎IM開發入門(一):什麼是IM系統?
(本文同步釋出於:http://www.52im.net/thread-38...)