IM系列2:消息送達保證機制實現(xiàn)(二):保證離線消息的可靠投遞

1、前言

本文的上篇《IM消息送達保證機制實現(xiàn)(一):保證在線實時消息的可靠投遞》中擂橘,我們討論了在線實時消息的投遞可以通過應用層的確認盏檐、發(fā)送方的超時重傳睹簇、接收方的去重等手段來保證業(yè)務層面消息的不丟不重。

但實時在線投遞針對的是消息收發(fā)雙方都在線的情況(如當發(fā)送方用戶A發(fā)送消息給接收方用戶B時蕾盯,用戶B是在線的)幕屹,那如果消息的接收方用戶B不在線,系統(tǒng)是如何保證消息的可達性的呢级遭?這就是本文要討論的問題望拖。

2、IM開發(fā)干貨系列文章

本文是系列文章中的第2篇挫鸽,總目錄如下:

《IM消息送達保證機制實現(xiàn)(一):保證在線實時消息的可靠投遞》

《IM消息送達保證機制實現(xiàn)(二):保證離線消息的可靠投遞》(本文

《如何保證IM實時消息的“時序性”與“一致性”说敏?》

《IM單聊和群聊中的在線狀態(tài)同步應該用“推”還是“拉”?》

《IM群聊消息如此復雜丢郊,如何保證不丟不重盔沫?》

《一種Android端IM智能心跳算法的設(shè)計與實現(xiàn)探討(含樣例代碼)》

《移動端IM登錄時拉取數(shù)據(jù)如何作到省流量?》

《通俗易懂:基于集群的移動端IM接入層負載均衡方案分享》

《淺談移動端IM的多點登陸和消息漫游原理》

3枫匾、消息接收方不在線時的典型消息發(fā)送流程

如上圖所述架诞,通常此類情況下消息的發(fā)送流程如下:

Step 1:用戶A發(fā)送一條消息給用戶B;

Step 2:服務器查看用戶B的狀態(tài)干茉,發(fā)現(xiàn)B的狀態(tài)為“offline”(即B當前不在線)谴忧;

Step 3:服務器將此條消息以離線消息的形式持久化存儲到DB中(當然,具體的持久化方案可由您IM的具體技術(shù)實現(xiàn)為準);

Step 4:服務器返回用戶A“發(fā)送成功”ACK確認包(注:對于消息發(fā)送方而言沾谓,消息一旦落地存儲至DB就認為是發(fā)送成功了)委造。

關(guān)于 “Step 4” 的補充說明:

請一定要理解“Step 4”,因為現(xiàn)在無論是傳統(tǒng)的PC端IM(類似QQ這樣的——可以在UI上看到好友的在線均驶、離線狀態(tài))還是目前主流的移動端IM(強調(diào)的是用戶全時在線——即你看不到好友到底在線還是離線昏兆,反正給你的假像就是這個好友“應該”是在線的),消息發(fā)送出去后辣恋,無論是對方實時在線收到還是對方不在線而被服務端離線存儲了亮垫,對于發(fā)送方而言只要消息沒有因為網(wǎng)絡(luò)等原因莫名消失,就應該認為是“被收到了”伟骨。

從技術(shù)的角度講饮潦,消息接收方收到的消息應答ACK包的真正發(fā)起者,實際上有兩種可能性:一種是由接收方發(fā)出携狭、而另一種是由服務端代為發(fā)送(這在MobileIMSDK開源工程里被稱作“偽應答”)继蜡。

4、典型離線消息表的設(shè)計以及拉取離線消息的過程

① 存儲離線消看書的表主要字段大致如下:

-- 消息接收者ID

receiver_uidvarchar(50),

-- 消息的唯一指紋碼(即消息ID)逛腿,用于去重等場景稀并,單機情況下此id可能是個自增值、分布式場景下可能是類似于UUID這樣的東西

msg_idvarchar(70),

-- 消息發(fā)出時的時間戳(如果是個跨國IM单默,則此時間戳可能是GMT-0標準時間)? ? ?

send_timetime,

-- 消息發(fā)送者ID

sender_uidvarchar(50),

-- 消息類型(標識此條消息是:文本碘举、圖片還是語音留言等)

msg_typeint,

-- 消息內(nèi)容(如果是圖片或語音留言等類型,由此字段存放的可能是對應文件的存儲地址或CDN的訪問URL)

msg_contentvarchar(1024),

② 離線消息拉取模式:

接收方B要拉取發(fā)送方A給ta發(fā)送的離線消息搁廓,只需在receiver_uid(即接收方B的用戶ID), sender_uid(即發(fā)送方A的用戶ID)上查詢引颈,然后把離線消息刪除,再把消息返回B即可境蜕。

③ 離線消息的拉取蝙场,如果用SQL語句來描述的話,它可以是:

SELECT msg_id, send_time, msg_type, msg_content

FROM offline_msgs

WHERE receiver_uid = ? and sender_uid = ?

④ 離線拉取的整體流程如下圖所示:

Stelp 1:用戶B開始拉取用戶A發(fā)送給ta的離線消息粱年;

Stelp 2:服務器從DB(或?qū)某志没萜鳎┲欣‰x線消息售滤;

Stelp 3:服務器從DB(或?qū)某志没萜鳎┲邪央x線消息刪除;

Stelp 4:服務器返回給用戶B想要的離線消息台诗。

5完箩、上述流程存在的問題以及優(yōu)化方案

如果用戶B有很多好友,登陸時客戶端需要對所有好友進行離線消息拉取拉庶,客戶端與服務器交互次數(shù)就會比較多嗜憔。

① 拉取好友離線消息的客戶端偽代碼:

// 登陸時所有好友都要拉取

for(all uid in B’s friend-list){

? ? // 與服務器交互

? ? get_offline_msg(B,uid);?

}

② 優(yōu)化方案1:

先拉取各個好友的離線消息數(shù)量,真正用戶B進去看離線消息時氏仗,才往服務器發(fā)送拉取請求(手機端為了節(jié)省流量吉捶,經(jīng)常會使用這個按需拉取的優(yōu)化)夺鲜。

③ 優(yōu)化方案2:

如下圖所示,一次性拉取所有好友發(fā)送給用戶B的離線消息呐舔,到客戶端本地再根據(jù)sender_uid進行計算币励,這樣的話,離校消息表的訪問模式就變?yōu)?>只需要按照receiver_uid來查詢了珊拼。登錄時與服務器的交互次數(shù)降低為了1次食呻。

④ 方案小結(jié):

通常情況下,主流的的移動端IM(比如微信澎现、手Q等)通常都是以“優(yōu)化方案2”為主仅胞,因為移動網(wǎng)絡(luò)的不可靠性加上電量、流量等資源的昂貴性剑辫,能盡量一次性干完的事干旧,就盡可能一次搞定,從而提供整個APP的用戶體驗(對于移動端應用而言妹蔽,省電椎眯、省流量同樣是用戶體驗的一部分)。

6胳岂、消息接收方一次拉取大量離線消息導致速度慢编整、卡頓的解決方法

用戶B一次性拉取所有好友發(fā)給ta的離線消息,消息量很大時乳丰,一個請求包很大掌测、速度慢,容易卡頓怎么辦产园?

正如上圖所示赏半,我們可以分頁拉取:根據(jù)業(yè)務需求淆两,先拉取最新(或者最舊)的一頁消息,再按需一頁頁拉取拂酣,這樣便能很好地解決用戶體驗問題秋冰。

7、優(yōu)化離線消息的拉取過程婶熬,保證離線消息不會丟失

如何保證可達性剑勾,上述步驟第三步執(zhí)行完畢之后,第四個步驟離線消息返回給客戶端過程中赵颅,服務器掛點虽另,路由器丟消息,或者客戶端crash了饺谬,那離線消息豈不是丟了么(數(shù)據(jù)庫已刪除捂刺,用戶還沒收到)?

確實,如果按照上述的1族展、2森缠、3、4步流程仪缸,的確是的贵涵,那如何保證離線消息的絕對可靠性、可達性恰画?

如同在線消息的應用層ACK機制一樣宾茂,離線消息拉時,不能夠直接刪除數(shù)據(jù)庫中的離線消息拴还,而必須等應用層的離線消息ACK(說明用戶B真的收到離線消息了)跨晴,才能刪除數(shù)據(jù)庫中的離線消息。這個應用層的ACK可以通過實時消息通道告之服務端自沧,也可以通過服務端提供的REST接口坟奥,以更通用、簡單的方式通知服務端拇厢。

8爱谁、進一步優(yōu)化,解決重復拉取離線消息的問題

如果用戶B拉取了一頁離線消息孝偎,卻在ACK之前crash了访敌,下次登錄時會拉取到重復的離線消息么?

確實衣盾,拉取了離線消息卻沒有ACK寺旺,服務器不會刪除之前的離線消息,故下次登錄時系統(tǒng)層面還會拉取到势决。但在業(yè)務層面阻塑,可以根據(jù)msg_id去重。SMC理論:系統(tǒng)層面無法做到消息不丟不重果复,業(yè)務層面可以做到陈莽,對用戶無感知。

優(yōu)化后的拉取過程虽抄,如下圖所示:

9走搁、進一步優(yōu)化,降低離線拉取ACK帶來的額外與服務器的交互次數(shù)

假設(shè)有N頁離線消息迈窟,現(xiàn)在每個離線消息需要一個ACK私植,那么豈不是客戶端與服務器的交互次數(shù)又加倍了?有沒有優(yōu)化空間车酣?

如上圖所示曲稼,不用每一頁消息都ACK索绪,在拉取第二頁消息時相當于第一頁消息的ACK,此時服務器再刪除第一頁的離線消息即可躯肌,最后一頁消息再ACK一次(實際上:最后一頁拉取的肯定是空返回者春,這樣可以極大地簡化這個分頁過程,否則客戶端得知道當前離線消息的總頁數(shù)清女,而由于消息讀取延遲的存在钱烟,這個總頁數(shù)理論上并非絕對不變,從而加大了數(shù)據(jù)讀取不一致的可能性)嫡丙。這樣的效果是拴袭,不管拉取多少頁離線消息,只會多一個ACK請求曙博,與服務器多一次交互拥刻。

10、本文小結(jié)

正如本文中所列舉的問題所描述的那樣父泳,保證“離線消息”的可達性比大家想象的要復雜一些般哼,常見優(yōu)化總結(jié)如下:

1)對于同一個用戶B,一次性拉取所有用戶發(fā)給ta的離線消息惠窄,再在客戶端本地進行發(fā)送方分析蒸眠,相比按照發(fā)送方一個個進行消息拉取,能大大減少服務器交互次數(shù)杆融;

2)分頁拉取楞卡,先拉取計數(shù)再按需拉取,是無線端的常見優(yōu)化脾歇;

3)應用層的ACK蒋腮,應用層的去重,才能保證離線消息的不丟不重藕各;

4)下一頁的拉取池摧,同時作為上一頁的ACK,能夠極大減少與服務器的交互次數(shù)激况。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末险绘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子誉碴,更是在濱河造成了極大的恐慌,老刑警劉巖瓣距,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黔帕,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹈丸,警方通過查閱死者的電腦和手機成黄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門呐芥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奋岁,你說我怎么就攤上這事思瘟。” “怎么了闻伶?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵滨攻,是天一觀的道長。 經(jīng)常有香客問我蓝翰,道長光绕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任畜份,我火速辦了婚禮诞帐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爆雹。我一直安慰自己停蕉,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布钙态。 她就那樣靜靜地躺著慧起,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驯绎。 梳的紋絲不亂的頭發(fā)上完慧,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音剩失,去河邊找鬼屈尼。 笑死,一個胖子當著我的面吹牛拴孤,可吹牛的內(nèi)容都是我干的脾歧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼演熟,長吁一口氣:“原來是場噩夢啊……” “哼鞭执!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芒粹,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤兄纺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后化漆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體估脆,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年座云,在試婚紗的時候發(fā)現(xiàn)自己被綠了疙赠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片付材。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖圃阳,靈堂內(nèi)的尸體忽然破棺而出厌衔,到底是詐尸還是另有隱情,我是刑警寧澤捍岳,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布富寿,位于F島的核電站,受9級特大地震影響祟同,放射性物質(zhì)發(fā)生泄漏作喘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一晕城、第九天 我趴在偏房一處隱蔽的房頂上張望泞坦。 院中可真熱鬧,春花似錦砖顷、人聲如沸贰锁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豌熄。三九已至,卻和暖如春物咳,著一層夾襖步出監(jiān)牢的瞬間锣险,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工览闰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芯肤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓压鉴,卻偏偏與公主長得像崖咨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子油吭,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容