天天看點

從用戶端的角度來談談移動端IM的消息可靠性和送達機制1、前言2、關于作者3、相關文章4、TCP協定的可靠性之外還會出現消息丢失?5、用戶端方案1:應用層 Ack 消息6、用戶端方案2:應用層 Seq ID7、本文小結附錄:更多IM開發技術文章

1、前言

IM App 是我做過 App 類型裡複雜度最高的一類,裡面可供深究探讨的技術難點非常之多。這篇文章和大家聊下從移動端用戶端的角度所關注的IM消息可靠性和送達機制(因為我個人對移動用戶端的經驗積累的比較豐富嘛)。

學習交流:
- 即時通訊開發交流群: 320837163

[推薦]

- 移動端IM開發入門文章:《

新手入門一篇就夠:從零開發移動端IM

(本文同步釋出于:

http://www.52im.net/thread-1470-1-1.html

2、關于作者

作者網名:

Peak,畢業于浙江大學,現為Facebook iOS 工程師。

作者的github: https://github.com/music4kid 作者的部落格: http://mrpeak.cn/About/

3、相關文章

IM開發幹貨系列文章或許也值得您讀一讀,總目錄如下:
IM消息送達保證機制實作(一):保證線上實時消息的可靠投遞 IM消息送達保證機制實作(二):保證離線消息的可靠投遞 如何保證IM實時消息的“時序性”與“一緻性”? IM單聊和群聊中的線上狀态同步應該用“推”還是“拉”? IM群聊消息如此複雜,如何保證不丢不重? 一種Android端IM智能心跳算法的設計與實作探讨(含樣例代碼) 移動端IM登入時拉取資料如何作到省流量? 通俗易懂:基于叢集的移動端IM接入層負載均衡方案分享 淺談移動端IM的多點登陸和消息漫遊原理 IM開發基礎知識補課(一):正确了解前置HTTP SSO單點登陸接口的原理 IM開發基礎知識補課(二):如何設計大量圖檔檔案的服務端存儲架構? IM開發基礎知識補課(三):快速了解服務端資料庫讀寫分離原理及實踐建議

如果您是IM開發初學者,強烈建議首先閱讀《

》。

4、TCP協定的可靠性之外還會出現消息丢失?

如何確定 IM 不丢消息是個相對複雜的話題,從用戶端發送資料到伺服器,再從伺服器抵達目标用戶端,最終在 UI 成功展示,其間涉及的環節很多,這裡隻取其中一環「接收端如何確定消息不丢失」來探讨,粗略聊下我接觸過的兩種設計思路。

說到可靠抵達,第一反應會聯想到 TCP 的 reliability。資料可靠抵達是個通用性的問題,無論是網絡二進制流資料,還是上層的業務資料,都有可靠性保障問題,TCP 作為網絡基礎設施協定,其可靠性設計的可靠性是毋庸置疑的,我們就從 TCP 的可靠性說起。

在 TCP 這一層,所有 Sender 發送的資料,每一個 byte 都有标号(Sequence Number),每個 byte 在抵達接收端之後都會被接收端傳回一個确認資訊(Ack Number), 二者關系為 Ack = Seq + 1。簡單來說,如果 Sender 發送一個 Seq = 1,長度為 100 bytes 的包,那麼 receiver 會傳回一個 Ack = 101 的包,如果 Sender 收到了這個Ack 包,說明資料确實被 Receiver 收到了,否則 Sender 會采取某種政策重發上面的包。

第一個問題是:

現在的 IM App 幾乎都是走 TCP 通道,既然 TCP 本身是具備可靠性的,為什麼還會出現消息接收端(Receiver)丢失消息的情況,看下圖一目了然:

一句話總結上圖的含義:

網絡層的可靠性不等同于業務層的可靠性。

資料可靠抵達網絡層之後,還需要一層層往上移交處理,可能的處理有:

安全性校驗,binary 解析,model 建立,寫 db,存入 cache,UI 展示,以及一些 edge cases(斷網,使用者 logout,disk full,OOM,crash,關機。。) 等等,項目的 feature 越多,網絡層往上的處理出錯的可能性就越大。

舉個最簡單的場景為例子:

消息可靠抵達網絡層之後,寫 db 之前 App crash(不稀奇,是 App 都會 crash),雖然資料在網絡層可靠抵達了,但沒存進 db,下次使用者打開 App 消息自然就丢失了,如果不在業務層再增加可靠性保障,網絡層面不會重發,那麼意味着這條消息對于 Receiver 永遠丢失了。

有關TCP協定的更多技術文章,請參考以下連結:
TCP/IP詳解  -  第17章·TCP:傳輸控制協定 第18章·TCP連接配接的建立與終止 第21章·TCP的逾時與重傳 通俗易懂-深入了解TCP協定(上):理論基礎 通俗易懂-深入了解TCP協定(下):RTT、滑動視窗、擁塞處理 理論經典:TCP協定的3次握手與4次揮手過程詳解 高性能網絡程式設計(一):單台伺服器并發TCP連接配接數到底可以有多少 不為人知的網絡程式設計(一):淺析TCP協定中的疑難雜症(上篇) 不為人知的網絡程式設計(二):淺析TCP協定中的疑難雜症(下篇) 不為人知的網絡程式設計(三):關閉TCP連接配接時為什麼會TIME_WAIT、CLOSE_WAIT 不為人知的網絡程式設計(四):深入研究分析TCP的異常關閉 網絡程式設計懶人入門(一):快速了解網絡通信協定(上篇) 網絡程式設計懶人入門(二):快速了解網絡通信協定(下篇) 網絡程式設計懶人入門(三):快速了解TCP協定一篇就夠 現代移動端網絡短連接配接的優化手段總結:請求速度、弱網适應、安全保障 >>  更多同類文章 ……

業務層保障可以采取以下兩種方案,請繼續閱讀下一節。

5、用戶端方案1:應用層 Ack 消息

這個方案可以簡單了解為,将 TCP 的 Ack 流程再走一遍,在應用層也建構一個 Ack 消息,在應用層可靠性得到确認(一般以存入 db 為準,更準确說是事務送出成功的回調函數)之後再發送這個 Ack 消息,Server 收到應用層 Ack 消息之後才認為 Receiver 已收到,否則也采取某種政策重發消息。

具體到 IM App 當中,接收端接受到 Server 的 Message,将 Message 存入 db,在确認回調裡發送 Ack Receive 消息,Server 收到 Ack Receive 即認為消息已經可靠抵達,否則會在某個時機重新推送(比如用戶端重連伺服器時候 Pull,比如有新消息時 Server Push)。

6、用戶端方案2:應用層 Seq ID

這個方案和上面不同,但也是在應用層操作。我們個每個 Message 配置設定一個 Seq ID,這個 Seq ID 對于單個使用者的接受消息隊列來說是連續的,如果 Message A 和 Message B 是相鄰的,那麼 MsgBSeqID = MsgASeqID + 1。每次存入 db 的時候更新 db 裡的 LastReceivedSeqID,LastReceivedSeqID 即為上一條寫入資料庫消息的 Seq ID。

這麼做的好處是,每次從網絡層收到消息時,從 db 裡取出 LastReceivedSeqID,如果 LastReceivedSeqID = 新消息 Seq ID - 1,那麼說明應用層消息時連續的沒有發生丢失。還可以對收到的批量消息做預檢測,檢查消息隊列裡的 Seq ID 是否為聯系的,隻要存在任何一種不連續的 Seq ID 情況,就說明發送了丢失,此時接收端可以用 LastReceivedSeqID 從 Server 重新擷取準确的接受消息隊列。

這麼做的好處是避免了每次都需要發送一條 Ack 消息,壞處是應用層邏輯複雜之後,一旦出現 Seq ID 不連續的情況,會過度依賴于 refetch,難以分析問題出現的原因,refetch 一旦過于頻繁,其流量損耗極有可能大于 Ack 消息的資料量。

7、本文小結

消息的可靠抵達可以抽象為更一般意義上的可靠性問題,工程上總會碰到需要解決各種形式可靠性問題的場景,以經典計算機理論或者實踐為基礎來分析應用層的工程問題,可以舉一反三,藥到病除。

在工程上實踐可靠性,需要線了解工程的每一個環節以及資料如何在各個環節流動,接下來才是分析每一個環節資料出錯的可能性。檢驗可靠性的标準時「入袋為安」,存入 db 或者以其他方式持久化到 disk 當中,這樣才能保證用戶端每次都能正确讀取到消息。

另外,可靠性可以了解為兩方面:

一是資料可靠抵達(沒有任何中間資料被丢失);

二是正确抵達(沒有亂序或者資料更改)。

其實理論上 TCP 也不是 100% 可靠(資料有可能在傳輸時改變而無法被檢測到),而是 100% 工程上可靠(資料改變而不被檢測到時個極小機率的事件),這是另外一個有意思的話題。

附錄:更多IM開發技術文章

[1] 有關IM/推送的通信格式、協定的選擇: 簡述傳輸層協定TCP和UDP的差別 為什麼QQ用的是UDP協定而不是TCP協定? 移動端即時通訊協定選擇:UDP還是TCP? 如何選擇即時通訊應用的資料傳輸格式 強列建議将Protobuf作為你的即時通訊應用資料傳輸格式 全方位評測:Protobuf性能到底有沒有比JSON快5倍? 移動端IM開發需要面對的技術問題(含通信協定選擇) 簡述移動端IM開發的那些坑:架構設計、通信協定和用戶端 理論聯系實際:一套典型的IM通信協定設計詳解 58到家實時消息系統的協定設計等技術實踐分享 詳解如何在NodeJS中使用Google的Protobuf 技術掃盲:新一代基于UDP的低延時網絡傳輸層協定——QUIC詳解 [2] 有關IM/推送的心跳保活處理: 應用保活終極總結(一):Android6.0以下的雙程序守護保活實踐 應用保活終極總結(二):Android6.0及以上的保活實踐(程序防殺篇) 應用保活終極總結(三):Android6.0及以上的保活實踐(被殺複活篇) Android程序保活詳解:一篇文章解決你的所有疑問 Android端消息推送總結:實作原理、心跳保活、遇到的問題等 深入的聊聊Android消息推送這件小事 為何基于TCP協定的移動端IM仍然需要心跳保活機制? 微信團隊原創分享:Android版微信背景保活實戰分享(程序保活篇) 微信團隊原創分享:Android版微信背景保活實戰分享(網絡保活篇) 移動端IM實踐:實作Android版微信的智能心跳機制 移動端IM實踐:WhatsApp、Line、微信的心跳政策分析 [3] 有關WEB端即時通訊開發: 新手入門貼:史上最全Web端即時通訊技術原理詳解 Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE SSE技術詳解:一種全新的HTML5伺服器推送事件技術 Comet技術詳解:基于HTTP長連接配接的Web端實時通信技術 新手快速入門:WebSocket簡明教程 WebSocket詳解(一):初步認識WebSocket技術 WebSocket詳解(二):技術原理、代碼示範和應用案例 WebSocket詳解(三):深入WebSocket通信協定細節 WebSocket詳解(四):刨根問底HTTP與WebSocket的關系(上篇) WebSocket詳解(五):刨根問底HTTP與WebSocket的關系(下篇) WebSocket詳解(六):刨根問底WebSocket與Socket的關系 socket.io實作消息推送的一點實踐及思路 LinkedIn的Web端即時通訊實踐:實作單機幾十萬條長連接配接 Web端即時通訊技術的發展與WebSocket、Socket.io的技術實踐 Web端即時通訊安全:跨站點WebSocket劫持漏洞詳解(含示例代碼) 開源架構Pomelo實踐:搭建Web端高性能分布式IM聊天伺服器 使用WebSocket和SSE技術實作Web端消息推送 詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocket MobileIMSDK-Web的網絡層架構為何使用的是Socket.io而不是Netty? 理論聯系實際:從零了解WebSocket的通信原理、協定格式、安全性 [4] 有關IM架構設計: 淺談IM系統的架構設計 一套海量線上使用者的移動端IM架構設計實踐分享(含詳細圖文) 一套原創分布式即時通訊(IM)系統理論架構方案 從零到卓越:京東客服即時通訊系統的技術架構演進曆程 蘑菇街即時通訊/IM伺服器開發之架構選擇 騰訊QQ1.4億線上使用者的技術挑戰和架構演進之路PPT 微信背景基于時間序的海量資料冷熱分級架構設計實踐 微信技術總監談架構:微信之道——大道至簡(演講全文) 如何解讀《微信技術總監談架構:微信之道——大道至簡》 快速裂變:見證微信強大背景架構從0到1的演進曆程(一) 17年的實踐:騰訊海量産品的技術方法論 移動端IM中大規模群消息的推送如何保證效率、實時性? 現代IM系統中聊天消息的同步和存儲方案探讨 [5] 有關IM安全的文章: 即時通訊安全篇(一):正确地了解和使用Android端加密算法 即時通訊安全篇(二):探讨組合加密算法在IM中的應用 即時通訊安全篇(三):常用加解密算法與通訊安全講解 即時通訊安全篇(四):執行個體分析Android中密鑰寫死的風險 即時通訊安全篇(五):對稱加密技術在Android平台上的應用實踐 即時通訊安全篇(六):非對稱加密技術的原理與應用實踐 傳輸層安全協定SSL/TLS的Java平台實作簡介和Demo示範 理論聯系實際:一套典型的IM通信協定設計詳解(含安全層設計) 微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解 來自阿裡OpenIM:打造安全可靠即時通訊服務的技術實踐分享 簡述實時音視訊聊天中端到端加密(E2EE)的工作原理 移動端安全通信的利器——端到端加密(E2EE)技術詳解 通俗易懂:一篇掌握即時通訊的消息傳輸安全原理 [6] 開源實時音視訊技術WebRTC的文章: 開源實時音視訊技術WebRTC的現狀 簡述開源實時音視訊技術WebRTC的優缺點 訪談WebRTC标準之父:WebRTC的過去、現在和未來 良心分享:WebRTC 零基礎開發者教程(中文)[附件下載下傳] WebRTC實時音視訊技術的整體架構介紹 新手入門:到底什麼是WebRTC伺服器,以及它是如何聯接通話的? WebRTC實時音視訊技術基礎:基本架構和協定棧 淺談開發實時視訊直播平台的技術要點 [觀點] WebRTC應該選擇H.264視訊編碼的四大理由 基于開源WebRTC開發實時音視訊靠譜嗎?第3方SDK有哪些? 開源實時音視訊技術WebRTC中RTP/RTCP資料傳輸協定的應用 實時通信RTC技術棧之:視訊編解碼 開源實時音視訊技術WebRTC在Windows下的簡明編譯教程 網頁端實時音視訊技術WebRTC:看起來很美,但離生産應用還有多少坑要填? [7] 實時音視訊開發的其它精華資料: 即時通訊音視訊開發(一):視訊編解碼之理論概述 即時通訊音視訊開發(二):視訊編解碼之數字視訊介紹 即時通訊音視訊開發(三):視訊編解碼之編碼基礎 即時通訊音視訊開發(四):視訊編解碼之預測技術介紹 即時通訊音視訊開發(五):認識主流視訊編碼技術H.264 即時通訊音視訊開發(六):如何開始音頻編解碼技術的學習 即時通訊音視訊開發(七):音頻基礎及編碼原理入門 即時通訊音視訊開發(八):常見的實時語音通訊編碼标準 即時通訊音視訊開發(九):實時語音通訊的回音及回音消除概述 即時通訊音視訊開發(十):實時語音通訊的回音消除技術詳解 即時通訊音視訊開發(十一):實時語音通訊丢包補償技術詳解 即時通訊音視訊開發(十二):多人實時音視訊聊天架構探讨 即時通訊音視訊開發(十三):實時視訊編碼H.264的特點與優勢 即時通訊音視訊開發(十四):實時音視訊資料傳輸協定介紹 即時通訊音視訊開發(十五):聊聊P2P與實時音視訊的應用情況 即時通訊音視訊開發(十六):移動端實時音視訊開發的幾個建議 即時通訊音視訊開發(十七):視訊編碼H.264、VP8的前世今生 [8] IM開發綜合文章: 從用戶端的角度來談談移動端IM的消息可靠性和送達機制 騰訊技術分享:社交網絡圖檔的帶寬壓縮技術演進之路 IM開發基礎知識補課:正确了解前置HTTP SSO單點登陸接口的原理 移動端IM開發需要面對的技術問題 開發IM是自己設計協定用位元組流好還是字元流好? 請問有人知道語音留言聊天的主流實作方式嗎? 一個低成本確定IM消息時序的方法探讨 談談移動端 IM 開發中登入請求的優化 完全自已開發的IM該如何設計“失敗重試”機制? 微信對網絡影響的技術試驗及分析(論文全文) 即時通訊系統的原理、技術和應用(技術論文) 開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀 QQ音樂團隊分享:Android中的圖檔壓縮技術詳解(上篇) QQ音樂團隊分享:Android中的圖檔壓縮技術詳解(下篇) 騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖檔傳輸速度和成功率 騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇) 騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇) 如約而至:微信自用的移動端IM網絡層跨平台元件庫Mars已正式開源 基于社交網絡的Yelp是如何實作海量使用者圖檔的無損壓縮的? [9] 開源移動端IM技術架構資料: 開源移動端IM技術架構MobileIMSDK:快速入門 開源移動端IM技術架構MobileIMSDK:常見問題解答 開源移動端IM技術架構MobileIMSDK:壓力測試報告

繼續閱讀