• <strike id="fdgpu"><input id="fdgpu"></input></strike>
    <label id="fdgpu"></label>
    <s id="fdgpu"><code id="fdgpu"></code></s>

  • <label id="fdgpu"></label>
  • <span id="fdgpu"><u id="fdgpu"></u></span>

    <s id="fdgpu"><sub id="fdgpu"></sub></s>
    您當前的位置是:  首頁(yè) > 資訊 > 國內 >
     首頁(yè) > 資訊 > 國內 >

    【融云分析】 IM 即時(shí)通訊之鏈路保活

    2019-04-10 16:51:44   作者:   來(lái)源:CTI論壇   評論:0  點(diǎn)擊:


      眾所周知,IM 即時(shí)通訊是一項對即時(shí)性要求非常高的技術(shù),而保障消息即時(shí)到達的首要條件就是鏈路存活。那么在復雜的網(wǎng)絡(luò )環(huán)境和國內安卓手機被深度定制化的條件下,如何保障鏈路存活呢?本文詳解了融云安卓端 SDK 在基于 TCP 協(xié)議實(shí)現鏈路保活方面的探索和經(jīng)驗。
      IM 系統整體框架
      如上圖所示,為了保障鏈路存活,一套成熟的 IM 系統一般會(huì )包含消息鏈路和推送鏈路兩條長(cháng)連接通道。當有新消息到達時(shí),消息服務(wù)首先會(huì )判斷消息鏈路是否存活,如果消息鏈路處于存活狀態(tài),消息優(yōu)先從消息鏈路下發(fā)到客戶(hù)端,否則會(huì )被路由到推送服務(wù)器,由推送鏈路下發(fā)。
      綜上所述,鏈路保活涉及到消息鏈路和推送鏈路兩條鏈路的保活策略。基于這兩條鏈路使用場(chǎng)景的不同,保活策略上除了心跳機制是相同的,其它保活策略各有不同。下文將逐一解讀。
      鏈路保活的必要性
      基于 TCP 的 Socket 連接建立之后,如果不做任何處理,這個(gè)連接會(huì )長(cháng)時(shí)間存在并且可用嗎?答案是否定的。原因有兩點(diǎn):
      一、默認Socket 連接無(wú)法及時(shí)探測到鏈路的異常情況,即使將 Socket 的屬性參數 KeepAlive 設置為 True 仍然無(wú)法及時(shí)獲取到鏈路存活狀態(tài)。這是因為 Socket 的連接狀態(tài)是由一個(gè)狀態(tài)機進(jìn)行維護的,連接完畢后,雙方都會(huì )處于建立狀態(tài)。假如某臺服務(wù)器因為某些原因導致負載超高,無(wú)法及時(shí)響應業(yè)務(wù)請求,這時(shí) TCP 探測到的仍然是連接狀態(tài),而實(shí)際上此鏈路已經(jīng)不可用了。
      二、國內運營(yíng)商的 NAT 超時(shí)機制會(huì )把一定時(shí)間內沒(méi)有數據交互的連接斷開(kāi),這個(gè)時(shí)間可能只有幾分鐘,遠無(wú)法滿(mǎn)足我們的長(cháng)連接需求。
      通用保活機制-心跳機制
      基于以上原因,要維持 Socket 連接長(cháng)時(shí)間存活,就需要實(shí)現自己的保活機制。最通用的一種保活機制就是心跳機制。即客戶(hù)端每隔一段時(shí)間給服務(wù)器發(fā)送一個(gè)很小的數據包,根據能否收到服務(wù)器的響應來(lái)判斷鏈路的可用性。為了節省流量,這個(gè)包一般非常小,甚至沒(méi)有內容。
      那么客戶(hù)端如何實(shí)現定時(shí)發(fā)送心跳包呢?一般有兩種方式:
      一種是通過(guò) Java 里的 Timer 來(lái)實(shí)現。最基本的步驟如下:
    1. 建立一個(gè)要執行的任務(wù) TimerTask 。
    2. 創(chuàng )建一個(gè) Timer 實(shí)例,通過(guò) Timer 提供的 schedule() 方法,將 TimerTask 加入到定時(shí)器 Timer 中,設置每隔一段時(shí)間執行 TimerTask , 在 TimerTask 里發(fā)送心跳包。這種方式實(shí)現起來(lái)較簡(jiǎn)單,而且省電,不需要持有 WakeLock 。缺點(diǎn)也很明顯,長(cháng)時(shí)間在后臺,進(jìn)程被回收或者系統休眠后, Timer 機制隨之失效。
      另外一種方式是利用安卓系統的定時(shí)任務(wù)管理器 AlarmManager 循環(huán)執行發(fā)送心跳包的任務(wù)。這種方式不會(huì )因為系統休眠而失效,系統休眠后仍然可以通過(guò) WakeLock 喚醒,執行心跳任務(wù),因此相對 Timer 機制,這種方式比較費電,使用的時(shí)候一定要注意如下幾點(diǎn):
    • 首先根據需求合理使用 AlarmManager 的鬧鐘參數。鬧鐘各參數的區別參考下表:
    • 其次 AlarmManager 提供了 cancel() 方法,在設置新的定時(shí)任務(wù)前,通過(guò) cancel() 方法取消系統里設置的同類(lèi)型任務(wù),避免設置冗余任務(wù)。
    • 最后,安卓從 6.0 版本引入了 Doze 模式,并提供了新的鬧鐘設置方法 setExactAndAllowWhileIdle() ,通過(guò)該方法設置的鬧鐘時(shí)間,系統會(huì )智能調度,將各個(gè)應用設置的事務(wù)統一在一次喚醒中處理,以達到省電的目的。推薦在安卓 6.0 以上系統中,優(yōu)先使用該方法。
      消息鏈路保活機制
      消息鏈路作為收發(fā)消息的主要通道,需要最大程度保障鏈路的可用性。在鏈路不可用或者異常斷開(kāi)時(shí),能及時(shí)探測并啟動(dòng)重連等保障機制。基于以上特性,消息鏈路除了前面所說(shuō)的心跳機制外,還另外維護了兩套鏈路優(yōu)化機制:復合連接機制和重連機制。
      復合連接機制的基本步驟如下:
    1. 客戶(hù)端連接導航服務(wù)器,導航服務(wù)器會(huì )下發(fā)應用對應的配置信息,其中包括連接服務(wù)器的地址列表。
    2. 客戶(hù)端從第一個(gè)服務(wù)器地址嘗試連接,并啟動(dòng)超時(shí)機制,如果連接失敗或沒(méi)有及時(shí)收到服務(wù)響應, 則繼續嘗試連接下一個(gè)直到成功連接,將成功連接的地址保存到本地,作為最優(yōu)地址,后面連接時(shí)優(yōu)先使用此地址。通過(guò)這種機制,能保障客戶(hù)端優(yōu)先選用最優(yōu)鏈路,縮短連接時(shí)間。
      重連機制,則是指業(yè)務(wù)層在檢測到與服務(wù)器的連接斷開(kāi)后,嘗試 N 次重新連接服務(wù)器,首次斷開(kāi) 1 秒后會(huì )重新連接,如果仍然連接不成功,會(huì )在 2 秒后(重連間隔時(shí)間為上次重連間隔時(shí)間乘 2  )嘗試重新連接服務(wù)器,以此類(lèi)推當嘗試重連 N 次后,仍然連不上服務(wù)器將不再?lài)L試重新連接,只有在網(wǎng)絡(luò )情況發(fā)生變化或重新打開(kāi)應用時(shí)才會(huì )再次嘗試重連。
      推送鏈路保活機制
      推送鏈路作為消息到達的補充手段,要求盡可能延長(cháng)在后臺的存活時(shí)間。即使被殺后,仍然能被再次喚醒。 iOS 手機有 APNS 來(lái)達到以上效果,但安卓的官方推送系統 FCM 在國內基本不可用。那在國內安卓系統上如何保障推送到達呢?首先咱們需要先了解下安卓系統上進(jìn)程管理的兩大機制:
      一種是 LMK 機制,英文是 Low Memory Killer , 基于 Linux 的內存管理機制衍生而來(lái)。主要是通過(guò)進(jìn)程的 oom_adj 值來(lái)判定進(jìn)程的重要程度,從而決定是否回收這些進(jìn)程。 oom_adj 的值越低,代表重要度越高,比如 native 進(jìn)程, framework 層啟動(dòng)的系統進(jìn)程,優(yōu)先級一般都為負數。其次是前臺可見(jiàn)進(jìn)程,系統也不會(huì )回收。然而可見(jiàn)進(jìn)程退到后臺后, oom_adj 的值會(huì )立即升高,在系統定時(shí)清理時(shí)被殺。
      另外一種機制是安卓原生的權限管理機制( AppOps ),各大廠(chǎng)家在此基礎上又進(jìn)行了深度定制化,比如小米的安全中心,華為的手機管家等,都用來(lái)進(jìn)行權限管理。該權限管理機制運行在安卓系統的框架層,上層各應用的進(jìn)程如果想嘗試重新啟動(dòng),系統首先會(huì )去權限管理中心檢查該進(jìn)程有沒(méi)有自啟動(dòng)權限,如果有,才準予啟動(dòng)。否則,從框架層直接限制系統的啟動(dòng)。
      基于以上兩種機制,推送鏈路的保活也可分為兩大類(lèi):
      一、進(jìn)程保活。它的思路是根據 LMK 機制提高進(jìn)程優(yōu)先級,降低被殺的幾率。主要有以下幾種方法:
    • 監聽(tīng)黑屏事件,啟動(dòng) 1 像素透明 Activity ,使應用進(jìn)程轉為可視進(jìn)程,降低被殺概率。在屏幕亮時(shí),關(guān)閉該 Activity 。
    • 雙服務(wù)守護。 A 服務(wù)以 startForeground() 形式啟動(dòng),發(fā)送一個(gè)通知, B 服務(wù)同樣以  startForeground() 形式啟動(dòng),且發(fā)送和 A 相同 ID 的通知,然后在 B 服務(wù)里調用 stopForeground() 方法,取消通知。這樣 A 服務(wù)就會(huì )以前臺進(jìn)程的形式存活,且不影響用戶(hù)感知。
    • 根據文件鎖互斥原理,監視 Java  進(jìn)程存活狀態(tài),若被殺, Linux 層成功持有文件,則通過(guò) exec() 命令,打開(kāi)一個(gè)純 Linux 的可執行文件,開(kāi)啟一個(gè) Daemon 進(jìn)程, 該進(jìn)程因為從 Linux 層啟動(dòng),在安卓 5.0 之前,優(yōu)先級會(huì )比較高,不會(huì )被殺。在安卓 5.0 之后,該方式不再有效。
      二、進(jìn)程拉活的策略和安卓系統的 AppOps 機制有關(guān),一般有如下幾種:
    • 利用 Service 本身的 Sticky 屬性,在 Service 的 onStartCommand() 中返回 START_STICKY ,這樣當 Service 被殺掉后,系統會(huì )自動(dòng)嘗試重啟。不過(guò)在國內定制化的系統上,這種方式能成功重啟的幾率很低,需要用戶(hù)在權限管理中心打開(kāi)自啟動(dòng)等權限,才能成功拉活。
    • 也就是前面講過(guò)的心跳機制,不過(guò)這里要求使用 AlarmManager 設置  ELAPSED_REALTIME_WAKEUP 屬性的鬧鐘,在系統休眠后,才會(huì )正常接受到心跳事件,從而將進(jìn)程拉活。
    • 通過(guò)監聽(tīng)網(wǎng)絡(luò )切換,用戶(hù)行為等事件,拉起進(jìn)程。
    • 應用間互相拉活。比如系統里有好幾個(gè)應用集成了同一個(gè) SDK , 那么在用戶(hù)啟動(dòng)其中某一個(gè) App 的時(shí)候, SDK 會(huì )去掃描其它應用,把“兄弟姐妹” 拉活。這種方式對用戶(hù)體驗傷害非常大,會(huì )造成系統莫名其妙的耗電。
      隨著(zhù)安卓系統版本的迭代,對后臺進(jìn)程的啟動(dòng)管控越來(lái)越嚴。為了解決推送的問(wèn)題,各手機廠(chǎng)家推出了自己的系統級推送服務(wù)。由廠(chǎng)家在 Framework 層統一維護一條推送通道,上層所有應用共同使用該推送鏈路,不需要再維護單獨進(jìn)程。當前支持系統級推送的廠(chǎng)家有:小米、華為、魅族、 vivo 、OPPO ,集成第三方系統級推送之后,整個(gè)消息的收發(fā)流程可以參考下圖:
      這種系統級別的推送省電,省內存,到達率高。應用可以根據手機型號的不同,優(yōu)先使用廠(chǎng)家系統級別的推送,再配合自身的保活機制,最大程度保障推送的到達率。
    【免責聲明】本文僅代表作者本人觀(guān)點(diǎn),與CTI論壇無(wú)關(guān)。CTI論壇對文中陳述、觀(guān)點(diǎn)判斷保持中立,不對所包含內容的準確性、可靠性或完整性提供任何明示或暗示的保證。請讀者僅作參考,并請自行承擔全部責任。

    專(zhuān)題

    CTI論壇會(huì )員企業(yè)

    亚洲精品网站在线观看不卡无广告,国产a不卡片精品免费观看,欧美亚洲一区二区三区在线,国产一区二区三区日韩 永登县| 迭部县| 六枝特区| 清远市| 大理市| 鄯善县| 东至县| 永城市| 华池县| 大姚县| 揭阳市| 博客| 渝中区| 昆山市| 锡林浩特市| 大庆市| 库尔勒市| 铜川市| 铅山县| 阿巴嘎旗| 会泽县| 揭阳市| 乡宁县| 武冈市| 科尔| 宁安市| 新余市| 筠连县| 民丰县| 峨边| 寿阳县| 明星| 习水县| 洛隆县| 沐川县| 名山县| 余干县| 武夷山市| 霍邱县| 灵川县| 涞水县| http://444 http://444 http://444 http://444 http://444 http://444