Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 499, comments - 13, trackbacks - 0, articles - 1

          本文來自融云技術團隊原創(chuàng)分享,原文發(fā)布于“ 融云全球互聯(lián)網通信云”公眾號,原題《IM 即時通訊之鏈路?;睢?,即時通訊網收錄時有部分改動。

          1、引言

          眾所周知,IM 即時通訊是一項對即時性要求非常高的技術,而保障消息即時到達的首要條件就是鏈路存活。那么在復雜的網絡環(huán)境和國內安卓手機被深度定制化的條件下,如何保障鏈路存活呢?本文詳解了融云安卓端IM產品在基于 TCP 協(xié)議實現(xiàn)鏈路?;罘矫娴膶嵺`總結。

          學習交流:

          - 即時通訊/推送技術開發(fā)交流5群:215477170 [推薦]

          - 移動端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動端IM

          (本文同步發(fā)布于:http://www.52im.net/thread-2744-1-1.html

          2、相關文章

          3、IM 系統(tǒng)整體框架

          如上圖所示,為了保障鏈路存活,一套成熟的 IM 系統(tǒng)一般會包含消息鏈路和推送鏈路兩條長連接通道。

          當有新消息到達時,消息服務首先會判斷消息鏈路是否存活,如果消息鏈路處于存活狀態(tài),消息優(yōu)先從消息鏈路下發(fā)到客戶端,否則會被路由到推送服務器,由推送鏈路下發(fā)。

          綜上所述:鏈路保活涉及到消息鏈路和推送鏈路兩條鏈路的?;畈呗??;谶@兩條鏈路使用場景的不同,保活策略上除了心跳機制是相同的,其它?;畈呗愿饔胁煌O旅鎸⒅鹨唤庾x。

          4、鏈路?;畹谋匾?/h1>

          基于 TCP 的 Socket 連接建立之后,如果不做任何處理,這個連接會長時間存在并且可用嗎?答案是否定的。

          原因有兩點:

          1)默認Socket 連接無法及時探測到鏈路的異常情況,即使將 Socket 的屬性參數 KeepAlive 設置為 True 仍然無法及時獲取到鏈路存活狀態(tài)。這是因為 Socket 的連接狀態(tài)是由一個狀態(tài)機進行維護的,連接完畢后,雙方都會處于建立狀態(tài)。假如某臺服務器因為某些原因導致負載超高,無法及時響應業(yè)務請求,這時 TCP 探測到的仍然是連接狀態(tài),而實際上此鏈路已經不可用了。

          2)國內運營商的 NAT 超時機制會把一定時間內沒有數據交互的連接斷開,這個時間可能只有幾分鐘,遠無法滿足我們的長連接需求。

          這方面更詳細的技術文章,請見:為何基于TCP協(xié)議的移動端IM仍然需要心跳保活機制?》、《微信團隊原創(chuàng)分享:Android版微信后臺保活實戰(zhàn)分享(網絡?;钇?

          5、通用保活機制-心跳機制

          基于以上原因,要維持 Socket 連接長時間存活,就需要實現(xiàn)自己的?;顧C制。

          最通用的一種?;顧C制就是心跳機制。即客戶端每隔一段時間給服務器發(fā)送一個很小的數據包,根據能否收到服務器的響應來判斷鏈路的可用性。為了節(jié)省流量,這個包一般非常?。ㄍǔJ窃叫≡胶茫热缇W易云信的IM云產品中1字節(jié)心跳包是作為產品賣點進行宣傳的),甚至沒有內容。

          那么客戶端如何實現(xiàn)定時發(fā)送心跳包呢?一般有兩種方式。

          一種是通過 Java 里的 Timer 來實現(xiàn)。

          最基本的步驟如下:

          1)建立一個要執(zhí)行的任務 TimerTask ;

          2)創(chuàng)建一個 Timer 實例,通過 Timer 提供的 schedule() 方法,將 TimerTask 加入到定時器 Timer 中,設置每隔一段時間執(zhí)行 TimerTask , 在 TimerTask 里發(fā)送心跳包。這種方式實現(xiàn)起來較簡單,而且省電,不需要持有 WakeLock 。缺點也很明顯,長時間在后臺,進程被回收或者系統(tǒng)休眠后, Timer 機制隨之失效。

          另外一種方式是利用安卓系統(tǒng)的定時任務管理器 AlarmManager 循環(huán)執(zhí)行發(fā)送心跳包的任務。

          這種方式不會因為系統(tǒng)休眠而失效,系統(tǒng)休眠后仍然可以通過 WakeLock 喚醒,執(zhí)行心跳任務。

          因此相對 Timer 機制,這種方式比較費電,使用的時候一定要注意如下幾點:

          1)首先根據需求合理使用 AlarmManager 的鬧鐘參數。鬧鐘各參數的區(qū)別參考下表:

          2)其次 AlarmManager 提供了 cancel() 方法,在設置新的定時任務前,通過 cancel() 方法取消系統(tǒng)里設置的同類型任務,避免設置冗余任務。

          最后,安卓從 6.0 版本引入了 Doze 模式,并提供了新的鬧鐘設置方法 setExactAndAllowWhileIdle() ,通過該方法設置的鬧鐘時間,系統(tǒng)會智能調度,將各個應用設置的事務統(tǒng)一在一次喚醒中處理,以達到省電的目的。推薦在安卓 6.0 以上系統(tǒng)中,優(yōu)先使用該方法。

          這方面更詳細的技術文章,請見:

          應用?;罱K極總結(一):Android6.0以下的雙進程守護?;顚嵺`

          應用保活終極總結(二):Android6.0及以上的保活實踐(進程防殺篇)

          應用保活終極總結(三):Android6.0及以上的?;顚嵺`(被殺復活篇)

          Android進程?;钤斀猓阂黄恼陆鉀Q你的所有疑問

          Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢

          全面盤點當前Android后臺?;罘桨傅恼鎸嵾\行效果(截止2019年前)

          6、消息鏈路?;顧C制

          消息鏈路作為收發(fā)消息的主要通道,需要最大程度保障鏈路的可用性。在鏈路不可用或者異常斷開時,能及時探測并啟動重連等保障機制。

          基于以上特性,消息鏈路除了前面所說的心跳機制外,還另外維護了兩套鏈路優(yōu)化機制:復合連接機制和重連機制。

          復合連接機制的基本步驟如下:

          1)客戶端連接導航服務器,導航服務器會下發(fā)應用對應的配置信息,其中包括連接服務器的地址列表;

          2)客戶端從第一個服務器地址嘗試連接,并啟動超時機制,如果連接失敗或沒有及時收到服務響應, 則繼續(xù)嘗試連接下一個直到成功連接,將成功連接的地址保存到本地,作為最優(yōu)地址,后面連接時優(yōu)先使用此地址。通過這種機制,能保障客戶端優(yōu)先選用最優(yōu)鏈路,縮短連接時間。

          ▲ 復合連接機制原理

          重連機制:則是指業(yè)務層在檢測到與服務器的連接斷開后,嘗試 N 次重新連接服務器,首次斷開 1 秒后會重新連接,如果仍然連接不成功,會在 2 秒后(重連間隔時間為上次重連間隔時間乘 2  )嘗試重新連接服務器,以此類推當嘗試重連 N 次后,仍然連不上服務器將不再嘗試重新連接,只有在網絡情況發(fā)生變化或重新打開應用時才會再次嘗試重連。

          ▲ 重連機制原理

          7、推送鏈路?;顧C制

          推送鏈路作為消息到達的補充手段,要求盡可能延長在后臺的存活時間。即使被殺后,仍然能被再次喚醒。 iOS 手機有 APNS 來達到以上效果(詳見《了解iOS消息推送一文就夠:史上最全iOS Push技術詳解》),但安卓的官方推送系統(tǒng) FCM 在國內基本不可用。那在國內安卓系統(tǒng)上如何保障推送到達呢?

          首先咱們需要先了解下安卓系統(tǒng)上進程管理的兩大機制:

          1)一種是 LMK 機制,英文是 Low Memory Killer , 基于 Linux 的內存管理機制衍生而來。主要是通過進程的 oom_adj 值來判定進程的重要程度,從而決定是否回收這些進程。 oom_adj 的值越低,代表重要度越高,比如 native 進程, framework 層啟動的系統(tǒng)進程,優(yōu)先級一般都為負數。其次是前臺可見進程,系統(tǒng)也不會回收。然而可見進程退到后臺后, oom_adj 的值會立即升高,在系統(tǒng)定時清理時被殺;

          2)另外一種機制是安卓原生的權限管理機制( AppOps ),各大廠家在此基礎上又進行了深度定制化,比如小米的安全中心,華為的手機管家等,都用來進行權限管理。該權限管理機制運行在安卓系統(tǒng)的框架層,上層各應用的進程如果想嘗試重新啟動,系統(tǒng)首先會去權限管理中心檢查該進程有沒有自啟動權限,如果有,才準予啟動。否則,從框架層直接限制系統(tǒng)的啟動。

          基于以上兩種機制,推送鏈路的保活也可分為兩大類。

          第一類:進程保活:

          它的思路是根據 LMK 機制提高進程優(yōu)先級,降低被殺的幾率。

          主要有以下幾種方法:

          1.1)監(jiān)聽黑屏事件,啟動 1 像素透明 Activity :使應用進程轉為可視進程,降低被殺概率。在屏幕亮時,關閉該 Activity 。

          1.2)雙服務守護: A 服務以 startForeground() 形式啟動,發(fā)送一個通知, B 服務同樣以  startForeground() 形式啟動,且發(fā)送和 A 相同 ID 的通知,然后在 B 服務里調用 stopForeground() 方法,取消通知。這樣 A 服務就會以前臺進程的形式存活,且不影響用戶感知。

          1.3)根據文件鎖互斥原理,監(jiān)視 Java  進程存活狀態(tài):若被殺, Linux 層成功持有文件,則通過 exec() 命令,打開一個純 Linux 的可執(zhí)行文件,開啟一個 Daemon 進程, 該進程因為從 Linux 層啟動,在安卓 5.0 之前,優(yōu)先級會比較高,不會被殺。在安卓 5.0 之后,該方式不再有效。

          第二類:進程拉活的策略和安卓系統(tǒng)的 AppOps 機制有關:

          一般有如下幾種:

          1)利用 Service 本身的 Sticky 屬性,在 Service 的 onStartCommand() 中返回 START_STICKY ,這樣當 Service 被殺掉后,系統(tǒng)會自動嘗試重啟。不過在國內定制化的系統(tǒng)上,這種方式能成功重啟的幾率很低,需要用戶在權限管理中心打開自啟動等權限,才能成功拉活;

          2)也就是前面講過的心跳機制,不過這里要求使用 AlarmManager 設置  ELAPSED_REALTIME_WAKEUP 屬性的鬧鐘,在系統(tǒng)休眠后,才會正常接受到心跳事件,從而將進程拉活;

          3)通過監(jiān)聽網絡切換,用戶行為等事件,拉起進程;

          4)應用間互相拉活。比如系統(tǒng)里有好幾個應用集成了同一個 SDK , 那么在用戶啟動其中某一個 App 的時候, SDK 會去掃描其它應用,把“兄弟姐妹” 拉活。這種方式對用戶體驗傷害非常大,會造成系統(tǒng)莫名其妙的耗電。

          以下?;?#8220;黑科技”的詳細介紹文章,請詳讀:

          應用?;罱K極總結(一):Android6.0以下的雙進程守護?;顚嵺`

          應用保活終極總結(二):Android6.0及以上的?;顚嵺`(進程防殺篇)

          應用?;罱K極總結(三):Android6.0及以上的保活實踐(被殺復活篇)

          隨著安卓系統(tǒng)版本的迭代,對后臺進程的啟動管控越來越嚴。為了解決推送的問題,各手機廠家推出了自己的系統(tǒng)級推送服務。由廠家在 Framework 層統(tǒng)一維護一條推送通道,上層所有應用共同使用該推送鏈路,不需要再維護單獨進程。當前支持系統(tǒng)級推送的廠家有:小米、華為、魅族、 vivo 、OPPO 。

          鑒于Android系統(tǒng)對后臺進程管控越來越嚴,保活“黑科技”已經不怎么靈了:

          Android P正式版即將到來:后臺應用?;?、消息推送的真正噩夢

          全面盤點當前Android后臺?;罘桨傅恼鎸嵾\行效果(截止2019年前)

          集成第三方系統(tǒng)級推送之后,整個消息的收發(fā)流程可以參考下圖:

          這種系統(tǒng)級別的推送省電,省內存,到達率高。應用可以根據手機型號的不同,優(yōu)先使用廠家系統(tǒng)級別的推送,再配合自身的保活機制,最大程度保障推送的到達率。

          附錄:更多IM相關的技術文章匯總

          [1] IM開發(fā)相關的熱門技術問題綜合文章:
          新手入門一篇就夠:從零開發(fā)移動端IM
          移動端IM開發(fā)者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”
          移動端IM開發(fā)者必讀(二):史上最全移動弱網絡優(yōu)化方法總結
          從客戶端的角度來談談移動端IM的消息可靠性和送達機制
          現(xiàn)代移動端網絡短連接的優(yōu)化手段總結:請求速度、弱網適應、安全保障
          騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路
          小白必讀:閑話HTTP短連接中的Session和Token
          IM開發(fā)基礎知識補課:正確理解前置HTTP SSO單點登陸接口的原理
          移動端IM中大規(guī)模群消息的推送如何保證效率、實時性?
          移動端IM開發(fā)需要面對的技術問題
          開發(fā)IM是自己設計協(xié)議用字節(jié)流好還是字符流好?
          請問有人知道語音留言聊天的主流實現(xiàn)方式嗎?
          IM消息送達保證機制實現(xiàn)(一):保證在線實時消息的可靠投遞
          IM消息送達保證機制實現(xiàn)(二):保證離線消息的可靠投遞
          如何保證IM實時消息的“時序性”與“一致性”?
          一個低成本確保IM消息時序的方法探討
          IM單聊和群聊中的在線狀態(tài)同步應該用“推”還是“拉”?
          IM群聊消息如此復雜,如何保證不丟不重?
          談談移動端 IM 開發(fā)中登錄請求的優(yōu)化
          移動端IM登錄時拉取數據如何作到省流量?
          淺談移動端IM的多點登陸和消息漫游原理
          完全自已開發(fā)的IM該如何設計“失敗重試”機制?
          通俗易懂:基于集群的移動端IM接入層負載均衡方案分享
          微信對網絡影響的技術試驗及分析(論文全文)
          即時通訊系統(tǒng)的原理、技術和應用(技術論文)

          [2] 一些網絡編程基礎資料:
          TCP/IP詳解 - 第11章·UDP:用戶數據報協(xié)議
          TCP/IP詳解 - 第17章·TCP:傳輸控制協(xié)議
          TCP/IP詳解 - 第18章·TCP連接的建立與終止
          TCP/IP詳解 - 第21章·TCP的超時與重傳
          技術往事:改變世界的TCP/IP協(xié)議(珍貴多圖、手機慎點)
          通俗易懂-深入理解TCP協(xié)議(上):理論基礎
          通俗易懂-深入理解TCP協(xié)議(下):RTT、滑動窗口、擁塞處理
          高性能網絡編程(一):單臺服務器并發(fā)TCP連接數到底可以有多少
          高性能網絡編程(二):上一個10年,著名的C10K并發(fā)連接問題
          高性能網絡編程(三):下一個10年,是時候考慮C10M并發(fā)問題了
          高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索
          高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型
          高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型
          不為人知的網絡編程(一):淺析TCP協(xié)議中的疑難雜癥(上篇)
          不為人知的網絡編程(二):淺析TCP協(xié)議中的疑難雜癥(下篇)
          不為人知的網絡編程(三):關閉TCP連接時為什么會TIME_WAIT、CLOSE_WAIT
          不為人知的網絡編程(四):深入研究分析TCP的異常關閉
          不為人知的網絡編程(五):UDP的連接性和負載均衡
          不為人知的網絡編程(六):深入地理解UDP協(xié)議并用好它
          不為人知的網絡編程(七):如何讓不可靠的UDP變的可靠?
          不為人知的網絡編程(八):從數據傳輸層深度解密HTTP
          不為人知的網絡編程(九):理論聯(lián)系實際,全方位深入理解DNS
          網絡編程懶人入門(一):快速理解網絡通信協(xié)議(上篇)
          網絡編程懶人入門(二):快速理解網絡通信協(xié)議(下篇)
          網絡編程懶人入門(三):快速理解TCP協(xié)議一篇就夠
          網絡編程懶人入門(四):快速理解TCP和UDP的差異
          網絡編程懶人入門(五):快速理解為什么說UDP有時比TCP更有優(yōu)勢
          網絡編程懶人入門(六):史上最通俗的集線器、交換機、路由器功能原理入門
          網絡編程懶人入門(七):深入淺出,全面理解HTTP協(xié)議
          網絡編程懶人入門(八):手把手教你寫基于TCP的Socket長連接
          網絡編程懶人入門(九):通俗講解,有了IP地址,為何還要用MAC地址?
          現(xiàn)代移動端網絡短連接的優(yōu)化手段總結:請求速度、弱網適應、安全保障
          移動端IM開發(fā)者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”
          移動端IM開發(fā)者必讀(二):史上最全移動弱網絡優(yōu)化方法總結
          從HTTP/0.9到HTTP/2:一文讀懂HTTP協(xié)議的歷史演變和設計思路
          腦殘式網絡編程入門(一):跟著動畫來學TCP三次握手和四次揮手
          腦殘式網絡編程入門(二):我們在讀寫Socket時,究竟在讀寫什么?
          腦殘式網絡編程入門(三):HTTP協(xié)議必知必會的一些知識
          腦殘式網絡編程入門(四):快速理解HTTP/2的服務器推送(Server Push)
          腦殘式網絡編程入門(五):每天都在用的Ping命令,它到底是什么?
          腦殘式網絡編程入門(六):什么是公網IP和內網IP?NAT轉換又是什么鬼?
          >> 更多同類文章 ……

          [3] 有關推送技術方面的文章:
          iOS的推送服務APNs詳解:設計思路、技術原理及缺陷等
          信鴿團隊原創(chuàng):一起走過 iOS10 上消息推送(APNS)的坑
          Android端消息推送總結:實現(xiàn)原理、心跳?;?、遇到的問題等
          掃盲貼:認識MQTT通信協(xié)議
          一個基于MQTT通信協(xié)議的完整Android推送Demo
          IBM技術經理訪談:MQTT協(xié)議的制定歷程、發(fā)展現(xiàn)狀等
          求教android消息推送:GCM、XMPP、MQTT三種方案的優(yōu)劣
          移動端實時消息推送技術淺析
          掃盲貼:淺談iOS和Android后臺實時消息推送的原理和區(qū)別
          絕對干貨:基于Netty實現(xiàn)海量接入的推送服務技術要點
          移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)
          為何微信、QQ這樣的IM工具不使用GCM服務推送消息?
          極光推送系統(tǒng)大規(guī)模高并發(fā)架構的技術實踐分享
          從HTTP到MQTT:一個基于位置服務的APP數據通信實踐概述
          魅族2500萬長連接的實時消息推送架構的技術實踐分享
          專訪魅族架構師:海量長連接的實時消息推送系統(tǒng)的心得體會
          深入的聊聊Android消息推送這件小事
          基于WebSocket實現(xiàn)Hybrid移動應用的消息推送實踐(含代碼示例)
          一個基于長連接的安全可擴展的訂閱/推送服務實現(xiàn)思路
          實踐分享:如何構建一套高可用的移動端消息推送系統(tǒng)?
          Go語言構建千萬級在線的高并發(fā)消息推送系統(tǒng)實踐(來自360公司)
          騰訊信鴿技術分享:百億級實時消息推送的實戰(zhàn)經驗
          百萬在線的美拍直播彈幕系統(tǒng)的實時推送技術實踐之路
          京東京麥商家開放平臺的消息推送架構演進之路
          了解iOS消息推送一文就夠:史上最全iOS Push技術詳解
          基于APNs最新HTTP/2接口實現(xiàn)iOS的高性能消息推送(服務端篇)
          解密“達達-京東到家”的訂單即時派發(fā)技術原理和實踐
          技術干貨:從零開始,教你設計一個百萬級的消息推送系統(tǒng)
          >> 更多同類文章 ……

          (本文同步發(fā)布于:http://www.52im.net/thread-2744-1-1.html



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發(fā)交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發(fā)表評論。


          網站導航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 齐河县| 壤塘县| 尚志市| 五指山市| 汉中市| 黔东| 新干县| 江安县| 乌鲁木齐县| 额敏县| 苏尼特右旗| 闽侯县| 蓬莱市| 静宁县| 景宁| 明光市| 滁州市| 巴东县| 郓城县| 安宁市| 上林县| 漯河市| 崇仁县| 西青区| 腾冲县| 龙游县| 友谊县| 鄂温| 浦县| 潞城市| 苍溪县| 闸北区| 武定县| 上饶县| 武胜县| 堆龙德庆县| 长治市| 富阳市| 仙游县| 平舆县| 武隆县|