萬字長文:手把手教你實現一套高效的IM長連線自適應心跳保活機制
本文作者“Carson”,現就職於騰訊公司,原題“高效保活長連線:手把手教你實現自適應的心跳保活機制”,有較多修訂和改動。
1、引言
當要實現IM即時通訊聊天、訊息推送等高實時性需求時,我們一般會選擇長連線的通訊方式。
而真正當實現長連線方式時,會遇到很多技術問題,比如最常見的長連線保活問題。
今天,我將通過本篇文章,手把手教大家實現一套可自適應的心跳保活機制,從而能高效穩定地維持諸如IM聊天這類需求的長連線。
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121215774-1951114518.png)
2、相關文章
- 《為何基於TCP協議的移動端IM仍然需要心跳保活機制?》
- 《一文讀懂即時通訊應用中的網路心跳包機制:作用、原理、實現思路等》
- 《一種Android端IM智慧心跳演算法的設計與實現探討(含樣例程式碼)》
- 《自已開發IM有那麼難嗎?手把手教你自擼一個Andriod版簡易IM (有原始碼)》
- 《跟著原始碼學IM(一):手把手教你用Netty實現心跳機制、斷線重連機制》
- 《跟著原始碼學IM(五):正確理解IM長連線、心跳及重連機制,並動手實現》
3、什麼是長連線
認識長連線:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121223029-1659358837.png)
長連線的主要作是通過長時間保持雙方連線,從而:
- 1)提高通訊速度;
- 2)確保實時性;
- 3)避免短時間內重複連線所造成的通道資源和網路資源的浪費。
長連線與短連線的區別:
4、導致長連線斷開的原因
4.1 基本概念
從上節可知,在使用長連線的情況下,雙方的所有通訊都建立在1條長連線上(比如1次TCP連線)。所以,長連線需要持續保持雙方連線才可使得雙方持續通訊。
然而,實際情況是,長連線會存在斷開的情況。
這些斷開原因主要是:
- 1)長連線所在程序被殺死(這主要說的是移動端);
- 2)NAT超時;
- 3)網路狀態發生變化;
- 4)其他不可抗因素(網路狀態差、DHCP的租期等等 )。
下面,我將對每種原因進行分析。
4.2 具體分析
1)原因1:程序被殺死
當程序被殺死後,長連線也會隨之斷開。程序被殺在Andriod端是最常見的問題,限於篇幅就不在此展開這個話題,有興趣可以閱讀這篇:《Android P正式版即將到來:後臺應用保活、訊息推送的真正噩夢》。
2)原因2:NAT 超時(重點關注)
NAT超時現象如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121237192-614209390.png)
各運營商和地區的NAT超時時間如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121243264-996528288.png)
PS:上述資料來源於微信團隊的《移動端IM實踐:實現Android版微信的智慧心跳機制》一文,隨著4G、5G的普及,這些資料有可能已發生變化,請以實際測試結果為準。
特別注意:排除其他外因(網路切換、NAT超時、人為原因),TCP長連線在雙方都不斷開連線的情況上,本質上是不會自動中斷的(也就是不需要心跳包來維持,可以驗證一下:讓2臺電腦連上同1個Wifi,其中1臺做伺服器, 另1臺做客戶端連線伺服器(無設定KeepAlive)。只要電腦、路由器不斷網斷電,那麼,2臺電腦的長連線是不會自動中斷的)。
Jack Jiang注:上述論述可能不太準確,有新興趣的讀者可以詳讀《拔掉網線再插上,TCP連線還在嗎?一文即懂!》。
3)原因3:網路狀態發生變化
當移動客戶端網路狀態發生變化時(如行動網路 & Wifi切換、斷開、重連),也會使長連線斷開。
4)原因4:其他不可抗因素
如網路狀態差、DHCP的租期到期等等,都會使得長連線發生 偶然的斷開。DHCP的租期到期:對於 Android系統, DHCP到了租期後不會主動續約(繼續使用過期IP),從而導致長連線斷開。
5、高效維持長連線的解決方案
5.1 基本介紹
在瞭解長連線斷開原因後,針對這些原因,此處給出我的高效維持長連線的解決方案(如下圖所示)。
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121252830-1192363885.png)
為此,若需有效維持長連線,則需要做到:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121552170-1175582142.png)
說得簡單點,高效維持長連線的關鍵在於:
- 1)保活:處於連線狀態時要做到儘量不要斷;
- 2)重連:連線斷了之後要能繼續重連回來。
5.2 具體措施
1)措施1:程序保活
整體概括如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121603979-418976924.png)
PS:關於Android的程序保活,這個話題就很熱門了,感興趣可以順著下面的文章詳細讀一讀:
- 《應用保活終極總結(一):Android6.0以下的雙程序守護保活實踐》
- 《應用保活終極總結(二):Android6.0及以上的保活實踐(程序防殺篇)》
- 《應用保活終極總結(三):Android6.0及以上的保活實踐(被殺復活篇)》
- 《Android程序保活詳解:一篇文章解決你的所有疑問》
- 《微信團隊原創分享:Android版微信後臺保活實戰分享(程序保活篇)》
- 《Android P正式版即將到來:後臺應用保活、訊息推送的真正噩夢》
- 《全面盤點當前Android後臺保活方案的真實執行效果(截止2019年前)》
- 《2020年了,Android後臺保活還有戲嗎?看我如何優雅的實現!》
- 《史上最強Android保活思路:深入剖析騰訊TIM的程序永生技術》
- 《Android程序永生技術終極揭密:程序被殺底層原理、APP應對被殺技巧》
- 《Android保活從入門到放棄:乖乖引導使用者加白名單吧(附7大機型加白示例)》
2)措施2:心跳保活機制
這是本文的重點,下節開始會詳細解析
3)措施3:斷線重連機制
原理就是:檢測網路狀態變化並及時判斷連線的有效性。
具體實現:這個其實跟心跳保活機制是一套完整的邏輯,所以下面會在心跳保活機制中一起講解。
6、心跳保活機制簡介
心跳保活機制的整體介紹如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121651176-1435433946.png)
不過,很多人容易混淆把心跳機制和傳統的HTTP輪詢機制搞混。
下面給出二者區別:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121658514-1456832260.png)
7、主流IM的心跳機制分析和對比
對國、內外主流的移動IM產品(WhatsApp、Line、微信)進行了心跳機制的簡單分析和對比。
具體請看下圖:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121705248-854950973.png)
PS:以上資料來自於微信團隊分享的《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》一文。
8、心跳保活機制方案總體設計
下面,我將根據市面上主流的心跳機制,設計了一套心跳機制方案。
心跳機制方案的基本流程:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121713991-756661723.png)
對於心跳機制方案設計的主要考慮因素是:
- 1)要保證訊息的實時性;
- 2)要考慮耗費裝置的資源(網路流量、電量、CPU等等)。
從上圖可以看出,對於心跳機制方案設計的要點在於:
- 1)心跳包的規格(內容 & 大小);
- 2)心跳傳送的間隔時間;
- 3)斷線重連機制 (核心 = 如何 判斷長連線的有效性)。
在下面的方案設計中,將針對這3個問題給出詳細的解決方案。
9、心跳機制方案的詳細設計
9.1 心跳包的規格
為了減少流量並提高發送效率,需要精簡心跳包的設計。
主要從心跳包的內容和大小入手,設計原則具體如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121722376-384351024.png)
設計方案:
心跳包 = 1個攜帶少量資訊 & 大小在10位元組內的資訊包
9.2 心跳傳送的間隔時間
為了 防止NAT超時並減少裝置資源的消耗(網路流量、電量、CPU等等),心跳傳送的間隔時間是整個心跳機制方案設計的重點。
心跳傳送間隔時間的設計原則如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121731674-771854472.png)
9.3 最常用的心跳間隔方案
一般,最直接且常用的心跳傳送間隔時間設定方案多采用:“每隔估計 x 分鐘傳送心跳包1次”。其中,x <5分鐘即可(綜合主流移動IM產品,此處建議 x= 4分鐘)。
但是,這種方案存在一些問題:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121738018-305158905.png)
PS:關於固定心跳間隔的方案具體實現,可以詳細參考:
- 《跟著原始碼學IM(一):手把手教你用Netty實現心跳機制、斷線重連機制》;
- 《跟著原始碼學IM(五):正確理解IM長連線、心跳及重連機制,並動手實現》;
- 《自已開發IM有那麼難嗎?手把手教你自擼一個Andriod版簡易IM (有原始碼)》。
9.4 自適應心跳間隔方案
下面,我將詳細講解自適應心跳間隔時間的設計方案。
基本邏輯:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121755092-1338101574.png)
該方案需要解決的有2個核心問題。
1)如何自適應計算心跳間隔 從而使得心跳間隔 接近 當前NAT 超時時間?
答:不斷增加心跳間隔時間進行心跳應答測試,直到心跳失敗5次後,即可找出最接近 當前NAT 超時時間的心跳間隔時間。
具體請看下圖:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121802235-2043447098.png)
注:只有當心跳間隔 接近 NAT 超時時間 時,才能最大化平衡 長連線不中斷 & 裝置資源消耗最低的問題。
2)如何檢測 當前網路環境的NAT 超時時間 發生了變化 ?
答:當前傳送心跳包成功 的最大間隔時間(即最接近NAT超時時間的心跳間隔) 傳送失敗5次後,則判斷當前網路環境的NAT 超時時間 發生了變化。
具體請看下圖:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121810330-1113430972.png)
注:在檢測到 NAT 超時時間 發生變化後,重新自適應計算心跳間隔 從而使得心跳間隔 接近 NAT 超時時間
總結一下:統籌以上2個核心問題,總結出自適應心跳間隔時間設計方案為下圖:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121823808-193737905.png)
PS:關於自適應心跳機制的設計和實現,可以詳細參考:
10、斷線重連機制的實現
技術上來說:長連線的心跳保活依賴於心跳機制,在心跳機制起作用的情況下,適時啟動斷線重連機制,在心跳機制和斷線重連機制的共同作用下才能實現真正的心跳保活。但為了讓邏輯更清晰,我把斷線重連機制跟心跳機制單獨各作為一節來講解。本節講的是斷片線重連機制。
該機制的核心在於:如何判斷長連線的有效性。即:什麼情況下視為長連線斷線?
1)設計原則:
基本邏輯就是:判斷長連線是否有效的準則 = 伺服器是否返回心跳應答。
此處需要分清長連線的“存活 & 有效“狀態的區別:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121936941-1689213168.png)
2)具體方案:
實現思路:通過計數計算,若連續5次傳送心跳後,伺服器都無心跳應答,則視為長連線無效。
判斷流程:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121943967-1662135552.png)
3)網上流傳的方案:
在網上流傳著一些用於判斷長連線是否有效的方案,具體介紹如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518121953333-911715542.png)
至此,關於心跳保活機制已經講解完畢。
11、方案小結
有必要總結一下我在上兩節分享的心跳機制和斷線重連機制,這兩個機制組成了本文的長連線心跳保活完整邏輯。
設計方案:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122004316-1523646789.png)
流程設計:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122012604-419716556.png)
注:標識 “灰色” 的判斷流程參考上文描述。
12、進一步優化和完善心跳保活方案
12.1 基本情況
上兩節中的方案依然會存在技術缺陷,從而導致長連線斷開(比如:長連線本身不可用(此時重連多少次也沒用))。
下面將優化和完善上述方案,從而保證 客戶端與伺服器依然保持著通訊狀態。
優化點主要是:
- 1)確保當前網路的有效性和穩定性再開始長連線;
- 2)自適應計算心跳包間隔時間的時機。
12.2 確保網路的有效性和穩定性後再開始長連線
問題描述:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122040649-1128980372.png)
解決方案:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122048302-1986370852.png)
加入到原有的心跳保活機制主流程:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122055834-432738230.png)
12.3 自適應計算心跳包間隔時間的時機
問題描述:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122106057-1750564216.png)
方案設計:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122115293-1335397034.png)
加入到到原有的心跳保活機制主流程:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122122392-375296648.png)
12.4 小結一下
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122132450-1524884346.png)
13、額外思考:TCP協議自帶的KeepAlive機制能否替代心跳機制?
很多人認為,TCP 協議自身就有KeepAlive機制,為何基於它的通訊連結,仍需在應用層實現額外的心跳保活機制?
- 結論是:無法替代;
- 原因是:TCP KeepAlive機制的作用是檢測連線的有無(死活),但無法檢測連線是否有效。
注:“連線有效”的定義 = 雙方具備傳送 & 接收訊息的能力。
先來看看KeepAlive 機制是什麼:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122144195-266149605.png)
KeepAlive 的機制不可替代心跳機制的具體原因如下:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122151331-177669936.png)
特別注意:
- 1)KeepAlive 機制只是作業系統底層的一個被動機制,不應該被上層應用層使用;
- 2)當系統關閉一個由KeepAlive 機制檢查出來的死連線時,是不會主動通知上層應用的,只能通過呼叫相應IO操作的返回值中發現。
小結一下就是:KeepAlive機制無法代替心跳機制,需要在應用層 自己實現心跳機制以檢測長連線的有效性,從而高效維持長連線。
Jack Jiang注:關於TCP本身的KeepAlive機制,可能詳讀:
14、本文總結
看完本文後,相信在高效維持長連線的需求下,你可以完美地解決了!
本文方案的主體設計就是:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122200054-1075856970.png)
方案的優化和完善內容就是:
![](http://img2022.cnblogs.com/blog/1834368/202205/1834368-20220518122209040-52982200.png)
15、參考資料
[1] TCP/IP詳解 卷1:協議
[2] 為何基於TCP協議的移動端IM仍然需要心跳保活機制?
[4] 萬字長文,一篇吃透WebSocket:概念、原理、易錯常識、動手實踐
[5] 移動端IM實踐:實現Android版微信的智慧心跳機制
[6] 移動端IM實踐:WhatsApp、Line、微信的心跳策略分析
[7] 微信團隊原創分享:Android版微信後臺保活實戰分享(網路保活篇)
[8] 融雲技術分享:融雲安卓端IM產品的網路鏈路保活技術實踐
[9] 阿里IM技術分享(五):閒魚億級IM訊息系統的及時性優化實踐
[10] 2020年了,Android後臺保活還有戲嗎?看我如何優雅的實現!
學習交流:
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
- 開源IM框架原始碼:http://github.com/JackJiang2011/MobileIMSDK
(本文同步釋出於:http://www.52im.net/thread-3908-1-1.html)
- IM聊天系統安全手段之傳輸內容端到端加密技術
- 基於Netty,徒手擼IM(一):IM系統設計篇
- 微信團隊分享:微信後臺在海量併發請求下是如何做到不崩潰的
- 視訊直播技術乾貨:一文讀懂主流視訊直播系統的推拉流架構、傳輸協議等
- 萬字長文:手把手教你實現一套高效的IM長連線自適應心跳保活機制
- 實時音視訊入門學習:開源工程WebRTC的技術原理和使用淺析
- 阿里IM技術分享(三):閒魚億級IM訊息系統的架構演進之路
- IM技術分享:萬人群聊訊息投遞方案的思考和實踐
- IM開發技術學習:揭祕微信朋友圈這種資訊推流背後的系統設計
- 訊息推送技術乾貨:美團實時訊息推送服務的技術演進之路
- IM開發乾貨分享:網易雲信IM客戶端的聊天訊息全文檢索技術實踐
- 融雲技術分享:全面揭祕億級IM訊息的可靠投遞機制
- 開源移動端 IM 框架 MobileIMSDK v6.0 釋出!
- 企業微信的IM架構設計揭祕:訊息模型、萬人群、已讀回執、訊息撤回等
- 喜馬拉雅億級使用者量的離線訊息推送系統架構設計實踐
- 阿里技術分享:閒魚IM基於Flutter的移動端跨端改造實踐
- 理解IM訊息“可靠性”和“一致性”問題,以及解決方案探討
- 長連線閘道器技術專題(五):喜馬拉雅自研億級API閘道器技術實踐
- 網頁端IM通訊技術快速入門:短輪詢、長輪詢、SSE、WebSocket
- 長連線閘道器技術專題(四):愛奇藝WebSocket實時推送閘道器技術實踐