IM App 如何確保消息可靠抵達(dá)

IM App 是我做過 App 類型里復(fù)雜度最高的一類钟哥,里面可供深究探討的技術(shù)難點非常之多创肥。這篇文章和大家聊下消息可靠抵達(dá)機(jī)制达舒。

如何確保 IM 不丟消息是個相對復(fù)雜的話題,從客戶端發(fā)送數(shù)據(jù)到服務(wù)器瓤的,再從服務(wù)器抵達(dá)目標(biāo)客戶端休弃,最終在 UI 成功展示,其間涉及的環(huán)節(jié)很多圈膏,這里只取其中一環(huán)「接收端如何確保消息不丟失」來探討塔猾,粗略聊下我接觸過的兩種設(shè)計思路。

說到可靠抵達(dá)稽坤,第一反應(yīng)會聯(lián)想到 TCP 的 reliability丈甸。數(shù)據(jù)可靠抵達(dá)是個通用性的問題,無論是網(wǎng)絡(luò)二進(jìn)制流數(shù)據(jù)尿褪,還是上層的業(yè)務(wù)數(shù)據(jù)睦擂,都有可靠性保障問題,TCP 作為網(wǎng)絡(luò)基礎(chǔ)設(shè)施協(xié)議杖玲,其可靠性設(shè)計的可靠性是毋庸置疑的顿仇,我們就從 TCP 的可靠性說起。

在 TCP 這一層摆马,所有 Sender 發(fā)送的數(shù)據(jù)臼闻,每一個 byte 都有標(biāo)號(Sequence Number),每個 byte 在抵達(dá)接收端之后都會被接收端返回一個確認(rèn)信息(Ack Number)囤采, 二者關(guān)系為 Ack = Seq + 1述呐。簡單來說,如果 Sender 發(fā)送一個 Seq = 1蕉毯,長度為 100 bytes 的包乓搬,那么 receiver 會返回一個 Ack = 101 的包,如果 Sender 收到了這個Ack 包代虾,說明數(shù)據(jù)確實被 Receiver 收到了进肯,否則 Sender 會采取某種策略重發(fā)上面的包。

第一個問題是:現(xiàn)在的 IM App 幾乎都是走 TCP 通道棉磨,既然 TCP 本身是具備可靠性的坷澡,為什么還會出現(xiàn)消息接收端(Receiver)丟失消息的情況,看下圖一目了然:

image

一句話總結(jié)上圖的含義:網(wǎng)絡(luò)層的可靠性不等同于業(yè)務(wù)層的可靠性

數(shù)據(jù)可靠抵達(dá)網(wǎng)絡(luò)層之后频敛,還需要一層層往上移交處理,可能的處理有:安全性校驗馅扣,binary 解析斟赚,model 創(chuàng)建,寫 db差油,存入 cache拗军,UI 展示,以及一些 edge cases(斷網(wǎng)蓄喇,用戶 logout发侵,disk full,OOM妆偏,crash刃鳄,關(guān)機(jī)。钱骂。) 等等叔锐,項目的 feature 越多,網(wǎng)絡(luò)層往上的處理出錯的可能性就越大见秽。

舉個最簡單的場景為例子愉烙,消息可靠抵達(dá)網(wǎng)絡(luò)層之后,寫 db 之前 App crash(不稀奇解取,是 App 都會 crash)步责,雖然數(shù)據(jù)在網(wǎng)絡(luò)層可靠抵達(dá)了,但沒存進(jìn) db禀苦,下次用戶打開 App 消息自然就丟失了蔓肯,如果不在業(yè)務(wù)層再增加可靠性保障,網(wǎng)絡(luò)層面不會重發(fā)伦忠,那么意味著這條消息對于 Receiver 永遠(yuǎn)丟失了省核。業(yè)務(wù)層保障可以采取兩種方案:

方案一:應(yīng)用層 Ack 消息

這個方案可以簡單理解為,將 TCP 的 Ack 流程再走一遍昆码,在應(yīng)用層也構(gòu)建一個 Ack 消息气忠,在應(yīng)用層可靠性得到確認(rèn)(一般以存入 db 為準(zhǔn),更準(zhǔn)確說是事務(wù)提交成功的回調(diào)函數(shù))之后再發(fā)送這個 Ack 消息赋咽,Server 收到應(yīng)用層 Ack 消息之后才認(rèn)為 Receiver 已收到旧噪,否則也采取某種策略重發(fā)消息。

具體到 IM App 當(dāng)中脓匿,接收端接受到 Server 的 Message淘钟,將 Message 存入 db,在確認(rèn)回調(diào)里發(fā)送 Ack Receive 消息陪毡,Server 收到 Ack Receive 即認(rèn)為消息已經(jīng)可靠抵達(dá)米母,否則會在某個時機(jī)重新推送(比如客戶端重連服務(wù)器時候 Pull勾扭,比如有新消息時 Server Push)。

方案二:應(yīng)用層 Seq ID

這個方案和上面不同铁瞒,但也是在應(yīng)用層操作妙色。我們個每個 Message 分配一個 Seq ID,這個 Seq ID 對于單個用戶的接受消息隊列來說是連續(xù)的慧耍,如果 Message A 和 Message B 是相鄰的身辨,那么 MsgBSeqID = MsgASeqID + 1。每次存入 db 的時候更新 db 里的 LastReceivedSeqID芍碧,LastReceivedSeqID 即為上一條寫入數(shù)據(jù)庫消息的 Seq ID煌珊。

這么做的好處是,每次從網(wǎng)絡(luò)層收到消息時泌豆,從 db 里取出 LastReceivedSeqID定庵,如果 LastReceivedSeqID = 新消息 Seq ID - 1,那么說明應(yīng)用層消息時連續(xù)的沒有發(fā)生丟失践美。還可以對收到的批量消息做預(yù)檢測洗贰,檢查消息隊列里的 Seq ID 是否為聯(lián)系的,只要存在任何一種不連續(xù)的 Seq ID 情況陨倡,就說明發(fā)送了丟失敛滋,此時接收端可以用 LastReceivedSeqID 從 Server 重新獲取準(zhǔn)確的接受消息隊列。

這么做的好處是避免了每次都需要發(fā)送一條 Ack 消息兴革,壞處是應(yīng)用層邏輯復(fù)雜之后绎晃,一旦出現(xiàn) Seq ID 不連續(xù)的情況,會過度依賴于 refetch杂曲,難以分析問題出現(xiàn)的原因庶艾,refetch 一旦過于頻繁,其流量損耗極有可能大于 Ack 消息的數(shù)據(jù)量擎勘。

總結(jié)

消息的可靠抵達(dá)可以抽象為更一般意義上的可靠性問題咱揍,工程上總會碰到需要解決各種形式可靠性問題的場景,以經(jīng)典計算機(jī)理論或者實踐為基礎(chǔ)來分析應(yīng)用層的工程問題棚饵,可以舉一反三煤裙,藥到病除。

在工程上實踐可靠性噪漾,需要線了解工程的每一個環(huán)節(jié)以及數(shù)據(jù)如何在各個環(huán)節(jié)流動硼砰,接下來才是分析每一個環(huán)節(jié)數(shù)據(jù)出錯的可能性。檢驗可靠性的標(biāo)準(zhǔn)時「入袋為安」欣硼,存入 db 或者以其他方式持久化到 disk 當(dāng)中题翰,這樣才能保證客戶端每次都能正確讀取到消息。

另外,可靠性可以理解為兩方面豹障,一是數(shù)據(jù)可靠抵達(dá)(沒有任何中間數(shù)據(jù)被丟失)冯事,二是正確抵達(dá)(沒有亂序或者數(shù)據(jù)更改),其實理論上 TCP 也不是 100% 可靠(數(shù)據(jù)有可能在傳輸時改變而無法被檢測到)血公,而是 100% 工程上可靠(數(shù)據(jù)改變而不被檢測到時個極小概率的事件)桅咆,這是另外一個有意思的話題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坞笙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荚虚,更是在濱河造成了極大的恐慌薛夜,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件版述,死亡現(xiàn)場離奇詭異梯澜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)渴析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門晚伙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俭茧,你說我怎么就攤上這事咆疗。” “怎么了母债?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵午磁,是天一觀的道長。 經(jīng)常有香客問我毡们,道長迅皇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任衙熔,我火速辦了婚禮登颓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘红氯。我一直安慰自己框咙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布脖隶。 她就那樣靜靜地躺著扁耐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪产阱。 梳的紋絲不亂的頭發(fā)上婉称,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼王暗。 笑死悔据,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俗壹。 我是一名探鬼主播科汗,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绷雏!你這毒婦竟也來了头滔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤涎显,失蹤者是張志新(化名)和其女友劉穎坤检,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體期吓,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡早歇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了讨勤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箭跳。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖潭千,靈堂內(nèi)的尸體忽然破棺而出谱姓,到底是詐尸還是另有隱情,我是刑警寧澤脊岳,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布逝段,位于F島的核電站,受9級特大地震影響割捅,放射性物質(zhì)發(fā)生泄漏奶躯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一亿驾、第九天 我趴在偏房一處隱蔽的房頂上張望嘹黔。 院中可真熱鬧,春花似錦莫瞬、人聲如沸儡蔓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喂江。三九已至,卻和暖如春旁振,著一層夾襖步出監(jiān)牢的瞬間获询,已是汗流浹背涨岁。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留吉嚣,地道東北人梢薪。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像尝哆,于是被迫代替她去往敵國和親秉撇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

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