本文由融云技術(shù)團(tuán)隊(duì)原創(chuàng)分享叨咖,原題“IM 消息數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)設(shè)計(jì)”,內(nèi)容有修訂啊胶。
1甸各、引言
在如今的移動(dòng)互聯(lián)網(wǎng)時(shí)代,IM類產(chǎn)品已是我們生活中不可或缺的組成部分焰坪。像微信趣倾、釘釘、QQ等是典型的以 IM 為核心功能的社交產(chǎn)品某饰。另外也有一些應(yīng)用雖然IM功能不是核心儒恋,但I(xiàn)M能力也是其整個(gè)應(yīng)用極其重要的組成部分善绎,比如在線游戲、電商直播等應(yīng)用诫尽。
在IM技術(shù)應(yīng)用場(chǎng)景越來越廣泛的前提下禀酱,對(duì)即時(shí)通訊IM技術(shù)的學(xué)習(xí)和掌握就顯的越來越有必要。
在IM龐大的技術(shù)體系中箱锐,消息系統(tǒng)無疑是最核心的比勉,而消息系統(tǒng)中,最關(guān)鍵的部分是消息的分發(fā)和存儲(chǔ)驹止,而離線消息和歷史消息又是這個(gè)關(guān)鍵環(huán)節(jié)中不可回避的技術(shù)要點(diǎn)浩聋。
本文將基于IM消息系統(tǒng)的技術(shù)實(shí)踐,分享關(guān)于離線消息和歷史消息的正確理解臊恋,以及具體的技術(shù)配合和實(shí)踐衣洁,希望能為你的離線消息和歷史消息技術(shù)設(shè)計(jì)帶來最佳實(shí)踐靈感。
(本文同步發(fā)布于:http://www.52im.net/thread-3887-1-1.html)
2抖仅、相關(guān)文章
技術(shù)相關(guān)文章:
《閑魚IM的在線、離線聊天數(shù)據(jù)同步機(jī)制優(yōu)化實(shí)踐》
《閑魚億級(jí)IM消息系統(tǒng)的可靠投遞優(yōu)化實(shí)踐》
《一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(下篇):可靠性撤卢、有序性环凿、弱網(wǎng)優(yōu)化等》
融云技術(shù)團(tuán)隊(duì)分享的其它文章:
《融云安卓端IM產(chǎn)品的網(wǎng)絡(luò)鏈路保活技術(shù)實(shí)踐》
3放吩、IM消息投遞的一般做法
在通常的IM消息系統(tǒng)中智听,對(duì)于實(shí)時(shí)消息、離線消息渡紫、歷史消息大概都是下面這樣的技術(shù)思路到推。
對(duì)于在線用戶:消息會(huì)直接實(shí)時(shí)發(fā)送到在線的接收方,消息發(fā)送完成后惕澎,服務(wù)器端并不會(huì)對(duì)消息進(jìn)行落地存儲(chǔ)莉测。
而對(duì)于離線的用戶:服務(wù)器端會(huì)將消息存入到離線庫(kù),當(dāng)用戶登錄后唧喉,從離線庫(kù)中將離線消息拉走捣卤,然后服務(wù)器端將離線消息刪除。
這樣實(shí)現(xiàn)的缺點(diǎn)就是消息不持久化欣喧,導(dǎo)致消息無法支持消息漫游腌零,降低了消息的可靠性。
(PS:實(shí)際上唆阿,這其實(shí)也不能算是缺點(diǎn)益涧,因?yàn)橐恍﹫?chǎng)景下存儲(chǔ)歷史消息并不是必須的,所謂的消息漫游能力也不是必備的驯鳖,比如微信闲询。)
而在我們?cè)O(shè)計(jì)的消息系統(tǒng)中久免,服務(wù)器只要接收到了發(fā)送方發(fā)上來的消息,在轉(zhuǎn)發(fā)給接收方的同時(shí)也會(huì)在離線數(shù)據(jù)庫(kù)及歷史消息庫(kù)中進(jìn)行消息的落地存儲(chǔ)扭弧,而歷史消息的落地也就能支持消息漫游等相關(guān)功能了阎姥。
4、什么是離線消息和歷史消息鸽捻?
關(guān)于離線消息和歷史消息呼巴,在技術(shù)上,我們是這樣定義御蒲。
1)離線消息:
離線消息就是用戶(即接收方)在離線過程中收到的消息衣赶,這些消息大多是用戶比較關(guān)心的消息,具有一定的時(shí)效性厚满。
以我們的系統(tǒng)經(jīng)驗(yàn)來說府瞄,我們的離線消息默認(rèn)只保存最近七天的消息。
用戶(即接收方)在下次登錄后會(huì)全量獲取這些離線消息碘箍,然后在客戶端根據(jù)聊天會(huì)話進(jìn)行離線消息的UI展示(比如顯示一個(gè)未讀消息氣泡等)遵馆。
(PS:用戶離線的可能性在技術(shù)上其實(shí)是由很多種情況組成的,比如對(duì)方不在線丰榴、對(duì)方網(wǎng)絡(luò)斷掉了货邓、對(duì)方手機(jī)崩潰了、服務(wù)器發(fā)送時(shí)出錯(cuò)了等等四濒,嚴(yán)格來講——只要無法實(shí)時(shí)發(fā)送成的消息逻恐,都算“離線消息”。)
2)歷史消息:
歷史消息存儲(chǔ)了用戶所有的聊天消息峻黍,這些消息包括發(fā)出的消息以及接收到的消息。
在客戶端獲取歷史消息時(shí)拨匆,通常是按照會(huì)話進(jìn)行分頁獲取的姆涩。
以我們的系統(tǒng)經(jīng)驗(yàn)來說,歷史消息的存儲(chǔ)時(shí)間我們?cè)O(shè)計(jì)默認(rèn)為半年惭每,當(dāng)然這個(gè)時(shí)間可以按實(shí)際的產(chǎn)品運(yùn)營(yíng)規(guī)則來定骨饿,沒有硬性規(guī)定。
5台腥、IM消息的發(fā)送及存儲(chǔ)流程
以下是我們系統(tǒng)整體的消息發(fā)送及存儲(chǔ)流程:
如上圖所示:當(dāng)用戶發(fā)送聊天消息到服務(wù)器端后宏赘,首先會(huì)進(jìn)入到消息系統(tǒng)中,消息系統(tǒng)會(huì)對(duì)消息進(jìn)行分發(fā)以及存儲(chǔ)黎侈。
這個(gè)過程中:對(duì)于在線的接收方察署,會(huì)選擇直接推送消息。但是遇到接收方不在線或者是消息推送失敗的情況下峻汉,也會(huì)有另外的消息獲取方式贴汪,比如接收方會(huì)主動(dòng)向服務(wù)器拉取未收到的消息脐往。但是接收方何時(shí)來服務(wù)器拉取消息以及從哪里拉取是未知的,所以消息存入到離線庫(kù)的意義也就在這里扳埂。
消息系統(tǒng)存儲(chǔ)離線的過程中业簿,為了不影響整個(gè)系統(tǒng)的更為平穩(wěn),我們使用了MQ消息隊(duì)列進(jìn)行IO解偶阳懂,所以聊天消息實(shí)際上是異步存入到離線庫(kù)中的(通過MQ進(jìn)行慢IO解偶梅尤,這其實(shí)也是慣常做法)。
在分發(fā)完消息后:消息服務(wù)會(huì)同步一份消息數(shù)據(jù)到歷史消息服務(wù)中岩调,歷史消息服務(wù)同樣會(huì)對(duì)消息進(jìn)行落地存儲(chǔ)巷燥。
對(duì)于新的客戶端設(shè)備:會(huì)有同步消息的需求(所謂的消息漫游能力),而這也正是歷史消息的主要作用誊辉。在歷史消息庫(kù)中矾湃,客戶端是可以拉取任意會(huì)話的全量歷史消息的。
6堕澄、IM離線消息邀跃、歷史消息在存儲(chǔ)邏輯上的區(qū)別
6.1 概述
通過上面的圖中能清晰的看到:
1)離線消息我們存儲(chǔ)介質(zhì)選用的是?Redis;
2)歷史消息我們選用的是?HBase蛙紫。
對(duì)于為什么選用不同的存儲(chǔ)介質(zhì)拍屑,其實(shí)我們考慮的是離線消息和歷史消息不同的業(yè)務(wù)場(chǎng)景和讀寫模式。
下面我們重點(diǎn)介紹一下離線消息和歷史消息存儲(chǔ)的區(qū)別坑傅。
6.2 離線消息存儲(chǔ)模式——“擴(kuò)散寫”
離線消息的存儲(chǔ)模式我們用的是擴(kuò)散寫僵驰。
如上圖所示:每個(gè)用戶都有自己?jiǎn)为?dú)的收件箱和發(fā)件箱:
1)收件箱存放的是需要向這個(gè)接收端同步的所有消息;
2)發(fā)件箱里存放的是發(fā)送端發(fā)出的所有消息唁毒。
以單聊為例:聊天中的兩人會(huì)話中蒜茴,消息會(huì)產(chǎn)生兩次寫,即發(fā)送者的發(fā)件箱和接收端的收件箱浆西。
而在群的場(chǎng)景下:寫入會(huì)被更加的放大(擴(kuò)散)粉私,如果群里有 N 個(gè)人,那一條群消息就會(huì)被擴(kuò)散寫 N 次近零。
小結(jié)一下:
1)擴(kuò)散寫的優(yōu)點(diǎn)是:接收端的邏輯會(huì)非常清晰簡(jiǎn)單诺核,只需要從收件箱里讀取一次即可,大大降低了同步消息所需的讀的壓力久信;
2)擴(kuò)散寫的缺點(diǎn)是:寫入會(huì)被成指數(shù)地放大窖杀,特別是針對(duì)群這種場(chǎng)景。
6.3 歷史消息存儲(chǔ)模式——“擴(kuò)散讀”
歷史消息的存儲(chǔ)模式我們用的是擴(kuò)散讀裙士。
因?yàn)闅v史消息中入客,每個(gè)會(huì)話都保存了整個(gè)會(huì)話的全量消息。在擴(kuò)散讀這種模式下,每個(gè)會(huì)話的消息只保存一次痊项。
對(duì)比擴(kuò)散寫模式锅风,擴(kuò)散讀的優(yōu)點(diǎn)和缺點(diǎn)如下:
1)優(yōu)點(diǎn)是:寫入次數(shù)大大降低,特別是針對(duì)群消息鞍泉,只需要存一次即可皱埠;
2)缺點(diǎn)是:接收端接收消息非常的復(fù)雜和低效,因?yàn)檫@種模式客戶端想拉取到所有消息就只能每個(gè)會(huì)話同步一次咖驮,讀就會(huì)被放大边器,而且可能會(huì)產(chǎn)生很多次無效的讀,因?yàn)橛行?huì)話可能根本沒有新消息托修。
6.4 小結(jié)
在 IM 這種應(yīng)用場(chǎng)景下忘巧,通常會(huì)用到擴(kuò)散寫這種消息同步模型,一條消息產(chǎn)生一條睦刃,但是可能會(huì)被讀多次砚嘴,是典型的讀多寫少的場(chǎng)景。
一個(gè)優(yōu)化好的IM系統(tǒng)涩拙,必須從設(shè)計(jì)上平衡讀寫壓力际长,避免讀或者寫任意一個(gè)維度達(dá)到天花板。
當(dāng)然擴(kuò)散寫這種模式也有其弊端兴泥,比如萬人群工育,會(huì)導(dǎo)致一條消息,寫入了一萬次搓彻。
綜合來講:我們需要根據(jù)自己的業(yè)務(wù)場(chǎng)景做相應(yīng)設(shè)計(jì)選擇如绸,以我們的IM系統(tǒng)為例,就是是根據(jù)了離線和歷史消息的不同場(chǎng)景選擇了寫擴(kuò)散和讀擴(kuò)散的組合模式旭贬。適合的才是最好的怔接,沒有必要死搬硬套理論。
7稀轨、IM客戶端的拉取消息邏輯
7.1 離線消息拉取邏輯
對(duì)于IM客戶端而言蜕提,離線消息的獲取針對(duì)的是自己的整個(gè)離線消息,包括所有的會(huì)話(直白了說靶端,就是上線時(shí)拉取此次離線過程中的所有未收取的離線消息)。
離線消息的獲取是自上而下的方式(按時(shí)間序)凛膏,我們的經(jīng)驗(yàn)是一次獲取 200 條(PS:如果離線消息過多杨名,會(huì)分頁多次拉取,拉取1“次”可以理解為拉取1“頁”)猖毫。
在客戶端拉取離線消息的信令中台谍,需要帶上當(dāng)前客戶端緩存的消息的最大時(shí)間戳。
通過上節(jié)的圖我們應(yīng)該知道吁断,離線消息我們存儲(chǔ)的是一個(gè)線性結(jié)構(gòu)(指的是按時(shí)間順序)趁蕊,Server 會(huì)根據(jù)這個(gè)時(shí)間戳向下查找離線消息坞生。當(dāng)重裝或者新安裝 App 時(shí),客戶端的“當(dāng)前客戶端緩存的消息的最大時(shí)間戳”可以傳 0 上來掷伙。
Server 也會(huì)緩存客戶端拉取到的最后一條消息的時(shí)間戳是己,然后根據(jù)業(yè)務(wù)場(chǎng)景,客戶端類型等因素來決定從哪里開始拉取任柜,如果沒有拉取完 Server 會(huì)在拉取消息的應(yīng)答中帶相應(yīng)的標(biāo)記位卒废,告訴客戶端繼續(xù)拉取,客戶端循環(huán)拉取宙地,直到所有離線消息拉完摔认。
7.2 歷史消息拉取邏輯
歷史消息的獲取通常針對(duì)的是單一會(huì)話。
在拉取過程中宅粥,需要向服務(wù)端提交兩個(gè)參數(shù):
1)對(duì)方的 ID(如果是單聊的話就是對(duì)方的 UserID参袱,如果是群則是群組ID);
2)當(dāng)前會(huì)話的最前面消息的時(shí)間戳(即當(dāng)前會(huì)話最老一條消息的時(shí)間戳)秽梅。
Server據(jù)這兩個(gè)參數(shù)抹蚀,可以定位到這個(gè)客戶端的此會(huì)話,然后一次獲取 20 條歷史消息风纠。
消息的拉取時(shí)序上采用的是自下而上的方式(也就是時(shí)間序逆序)况鸣,即從最后面往前翻。只要有消息竹观,客戶端可以一直向前翻镐捧,手動(dòng)觸發(fā)獲取會(huì)話的歷史消息。
上面的拉取邏輯臭增,在IM界面功能上通常對(duì)應(yīng)的是下拉或點(diǎn)擊“加載更多”懂酱,比如這樣:
8、本文小結(jié)
本文主要分享了IM中有關(guān)離線消息和歷史消息的正確誊抛,主要包括離線消息和歷史消息的區(qū)別列牺,以及二者在存儲(chǔ)、分發(fā)拗窃、拉取邏輯方面的最佳踐等瞎领。如對(duì)文中內(nèi)容有異議,歡迎留言討論随夸。
9九默、參考資料
[1]?一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)
[2]?一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案
[3]?從零到卓越:京東客服即時(shí)通訊系統(tǒng)的技術(shù)架構(gòu)演進(jìn)歷程
[4]?一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[5]?閑魚億級(jí)IM消息系統(tǒng)的架構(gòu)演進(jìn)之路
[6]?閑魚億級(jí)IM消息系統(tǒng)的可靠投遞優(yōu)化實(shí)踐
[7]?閑魚億級(jí)IM消息系統(tǒng)的及時(shí)性優(yōu)化實(shí)踐
[8]?基于實(shí)踐:一套百萬消息量小規(guī)模IM系統(tǒng)技術(shù)要點(diǎn)總結(jié)
[9]?IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(一):保證在線實(shí)時(shí)消息的可靠投遞
[10]?理解IM消息“可靠性”和“一致性”問題宾毒,以及解決方案探討
[11]?零基礎(chǔ)IM開發(fā)入門(一):什么是IM系統(tǒng)驼修?
(本文同步發(fā)布于:http://www.52im.net/thread-3887-1-1.html)