IM App 是我作過 App 類型裏複雜度最高的一類,裏面可供深究探討的技術難點很是之多。這篇文章和你們聊下從移動端客戶端的角度所關注的IM消息可靠性和送達機制(由於我我的對移動客戶端的經驗積累的比較豐富嘛)。php
學習交流:html
- 即時通信開發交流羣:320837163[推薦]git
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》github
(本文同步發佈於:http://www.52im.net/thread-1470-1-1.html)算法
做者網名:Peak,畢業於浙江大學,現爲Facebook iOS 工程師。數據庫
做者的github:https://github.com/music4kid編程
做者的博客:http://mrpeak.cn/About/安全
IM開發乾貨系列文章或許也值得您讀一讀,總目錄以下:服務器
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》微信
《一種Android端IM智能心跳算法的設計與實現探討(含樣例代碼)》
《IM開發基礎知識補課(一):正確理解前置HTTP SSO單點登錄接口的原理》
若是您是IM開發初學者,強烈建議首先閱讀《新手入門一篇就夠:從零開發移動端IM》。
如何確保 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協議(下):RTT、滑動窗口、擁塞處理》
《高性能網絡編程(一):單臺服務器併發TCP鏈接數到底能夠有多少》
《鮮爲人知的網絡編程(一):淺析TCP協議中的疑難雜症(上篇)》
《鮮爲人知的網絡編程(二):淺析TCP協議中的疑難雜症(下篇)》
《鮮爲人知的網絡編程(三):關閉TCP鏈接時爲何會TIME_WAIT、CLOSE_WAIT》
《現代移動端網絡短鏈接的優化手段總結:請求速度、弱網適應、安全保障》
>> 更多同類文章 ……
業務層保障能夠採起如下兩種方案,請繼續閱讀下一節。
這個方案能夠簡單理解爲,將 TCP 的 Ack 流程再走一遍,在應用層也構建一個 Ack 消息,在應用層可靠性獲得確認(通常以存入 db 爲準,更準確說是事務提交成功的回調函數)以後再發送這個 Ack 消息,Server 收到應用層 Ack 消息以後才認爲 Receiver 已收到,不然也採起某種策略重發消息。
具體到 IM App 當中,接收端接受到 Server 的 Message,將 Message 存入 db,在確認回調裏發送 Ack Receive 消息,Server 收到 Ack Receive 即認爲消息已經可靠抵達,不然會在某個時機從新推送(好比客戶端重連服務器時候 Pull,好比有新消息時 Server Push)。
這個方案和上面不一樣,但也是在應用層操做。咱們個每一個 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 消息的數據量。
消息的可靠抵達能夠抽象爲更通常意義上的可靠性問題,工程上總會碰到須要解決各類形式可靠性問題的場景,以經典計算機理論或者實踐爲基礎來分析應用層的工程問題,能夠觸類旁通,藥到病除。
在工程上實踐可靠性,須要線瞭解工程的每個環節以及數據如何在各個環節流動,接下來纔是分析每個環節數據出錯的可能性。檢驗可靠性的標準時「入袋爲安」,存入 db 或者以其餘方式持久化到 disk 當中,這樣才能保證客戶端每次都能正確讀取到消息。
另外,可靠性能夠理解爲兩方面:
一是數據可靠抵達(沒有任何中間數據被丟失);
二是正確抵達(沒有亂序或者數據更改)。
其實理論上 TCP 也不是 100% 可靠(數據有可能在傳輸時改變而沒法被檢測到),而是 100% 工程上可靠(數據改變而不被檢測到時個極小機率的事件),這是另一個有意思的話題。
[1] 有關IM/推送的通訊格式、協議的選擇:
《強列建議將Protobuf做爲你的即時通信應用數據傳輸格式》
《全方位評測:Protobuf性能到底有沒有比JSON快5倍?》
《詳解如何在NodeJS中使用Google的Protobuf》
《技術掃盲:新一代基於UDP的低延時網絡傳輸層協議——QUIC詳解》
>> 更多同類文章 ……
[2] 有關IM/推送的心跳保活處理:
《應用保活終極總結(一):Android6.0如下的雙進程守護保活實踐》
《應用保活終極總結(二):Android6.0及以上的保活實踐(進程防殺篇)》
《應用保活終極總結(三):Android6.0及以上的保活實踐(被殺復活篇)》
《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》
《微信團隊原創分享:Android版微信後臺保活實戰分享(進程保活篇)》
《微信團隊原創分享:Android版微信後臺保活實戰分享(網絡保活篇)》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
>> 更多同類文章 ……
[3] 有關WEB端即時通信開發:
《Web端即時通信技術盤點:短輪詢、Comet、Websocket、SSE》
《Comet技術詳解:基於HTTP長鏈接的Web端實時通訊技術》
《WebSocket詳解(一):初步認識WebSocket技術》
《WebSocket詳解(二):技術原理、代碼演示和應用案例》
《WebSocket詳解(三):深刻WebSocket通訊協議細節》
《WebSocket詳解(四):刨根問底HTTP與WebSocket的關係(上篇)》
《WebSocket詳解(五):刨根問底HTTP與WebSocket的關係(下篇)》
《WebSocket詳解(六):刨根問底WebSocket與Socket的關係》
《LinkedIn的Web端即時通信實踐:實現單機幾十萬條長鏈接》
《Web端即時通信技術的發展與WebSocket、Socket.io的技術實踐》
《Web端即時通信安全:跨站點WebSocket劫持漏洞詳解(含示例代碼)》
《開源框架Pomelo實踐:搭建Web端高性能分佈式IM聊天服務器》
《詳解Web端通訊方式的演進:從Ajax、JSONP 到 SSE、Websocket》
《MobileIMSDK-Web的網絡層框架爲什麼使用的是Socket.io而不是Netty?》
《理論聯繫實際:從零理解WebSocket的通訊原理、協議格式、安全性》
>> 更多同類文章 ……
[4] 有關IM架構設計:
《一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)》
《IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》
《IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議》
>> 更多同類文章 ……
[5] 有關IM安全的文章:
《即時通信安全篇(一):正確地理解和使用Android端加密算法》
《即時通信安全篇(四):實例分析Android中密鑰硬編碼的風險》
《即時通信安全篇(五):對稱加密技術在Android平臺上的應用實踐》
《傳輸層安全協議SSL/TLS的Java平臺實現簡介和Demo演示》
《理論聯繫實際:一套典型的IM通訊協議設計詳解(含安全層設計)》
《微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解》
《來自阿里OpenIM:打造安全可靠即時通信服務的技術實踐分享》
《Web端即時通信安全:跨站點WebSocket劫持漏洞詳解(含示例代碼)》
>> 更多同類文章 ……
[6] 開源實時音視頻技術WebRTC的文章:
《訪談WebRTC標準之父:WebRTC的過去、如今和將來》
《良心分享:WebRTC 零基礎開發者教程(中文)[附件下載]》
《新手入門:到底什麼是WebRTC服務器,以及它是如何聯接通話的?》
《[觀點] WebRTC應該選擇H.264視頻編碼的四大理由》
《基於開源WebRTC開發實時音視頻靠譜嗎?第3方SDK有哪些?》
《開源實時音視頻技術WebRTC中RTP/RTCP數據傳輸協議的應用》
《開源實時音視頻技術WebRTC在Windows下的簡明編譯教程》
《網頁端實時音視頻技術WebRTC:看起來很美,但離生產應用還有多少坑要填?》
>> 更多同類文章 ……
[7] 實時音視頻開發的其它精華資料:
《即時通信音視頻開發(五):認識主流視頻編碼技術H.264》
《即時通信音視頻開發(九):實時語音通信的迴音及迴音消除概述》
《即時通信音視頻開發(十):實時語音通信的迴音消除技術詳解》
《即時通信音視頻開發(十一):實時語音通信丟包補償技術詳解》
《即時通信音視頻開發(十三):實時視頻編碼H.264的特色與優點》
《即時通信音視頻開發(十五):聊聊P2P與實時音視頻的應用狀況》
《即時通信音視頻開發(十六):移動端實時音視頻開發的幾個建議》
《即時通信音視頻開發(十七):視頻編碼H.26四、VP8的前世此生》
>> 更多同類文章 ……
[8] IM開發綜合文章:
《現代移動端網絡短鏈接的優化手段總結:請求速度、弱網適應、安全保障》
《IM開發基礎知識補課:正確理解前置HTTP SSO單點登錄接口的原理》
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《開源IM工程「蘑菇街TeamTalk」的現狀:一場虎頭蛇尾的開源秀》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)》
《騰訊原創分享(一):如何大幅提高移動網絡下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》
《如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》
《基於社交網絡的Yelp是如何實現海量用戶圖片的無損壓縮的?》
>> 更多同類文章 ……
[9] 開源移動端IM技術框架資料:
《開源移動端IM技術框架MobileIMSDK:常見問題解答》
《開源移動端IM技術框架MobileIMSDK:壓力測試報告》
>> 更多同類文章 ……
(本文同步發佈於:http://www.52im.net/thread-1470-1-1.html)