即時通信 IM 開發指南 2:心跳指令詳解

《即時通信 IM 開發指南》系列文章將會介紹一個 IM APP 的方方面面,包括技術選型、登錄優化等。此外,本文做者會結合他在網易雲信多年 iOS IM SDK 開發的經驗,深度分析實際開發中的各類常見問題。git

心跳指令是什麼?

在使用 TCP 長鏈接的 IM 服務設計中,每每都會涉及到心跳。心跳通常是指某端(絕大多數狀況下是客戶端)每隔必定時間向對端發送自定義指令,以判斷雙方是否存活,因其按照必定間隔發送,相似於心跳,故被稱爲心跳指令。github

  • 爲何須要在應用層作心跳?

那麼爲何須要在應用層作心跳,難道 TCP 不是個可靠鏈接嗎?咱們不可以依賴 TCP 作斷線檢測嗎?好比使用 TCP 的 KeepAlive 機制來實現。應用層心跳是目前的最佳實踐嗎?怎麼樣的心跳纔是最佳實踐?
是否是之前歷來沒有仔細考慮過這些問題,僅僅只是個簡單的心跳而已啊!
對於客戶端而言,使用 TCP 長鏈接來實現業務的最大驅動力在於:在當前鏈接可用的狀況下,每一次請求都只是簡單的數據發送和接受,免去了 DNS 解析,鏈接創建等時間,大大加快了請求的速度,同時也有利於接受服務器的實時消息。
但前提是鏈接可用。若是鏈接沒法很好地保持,每次請求就會變成撞大運:運氣好,經過長鏈接發送請求並收到反饋。運氣差,當前鏈接已失效,請求遲遲沒有收到反饋直到超時,又須要一次鏈接創建的過程,其效率甚至還不如 HTTP。而鏈接保持的前提必然是檢測鏈接的可用性,並在鏈接不可用時主動放棄當前鏈接並創建新的鏈接。
基於這個前提,必需要有一種機制用於檢測鏈接可用性。同時移動網絡的特殊性也要求客戶端須要在空餘時間發送必定的信令,避免鏈接被回收。詳見《微信和運營商的撕B》
而對於服務器而言,可以及時獲悉鏈接可用性也很是重要:一方面服務器須要及時清理無效鏈接以減輕負載,另外一方面也是業務的需求,如遊戲副本中服務器須要及時處理玩家掉線帶來的問題。
上面說了保持鏈接的重要性,那麼如今回到具體實現上。爲何咱們須要使用應用層心跳來作檢測,而不是直接使用 TCP 的特性呢?
咱們知道 TCP 是一個基於鏈接的協議,其鏈接狀態是由一個狀態機進行維護,鏈接完畢後,雙方都會處於 established 狀態,這以後的狀態並不會主動進行變化。這意味着若是上層不進行任何調用,一直使 TCP 鏈接空閒,那麼這個鏈接雖然沒有任何數據,但還是保持鏈接狀態,一天,一星期,甚至一個月,即便在這期間中間路由崩潰重啓無數次。舉個現實中常常遇到的栗子:當咱們 ssh 到本身的 VPS 上,而後不當心踢掉網線,此時的網絡變化並不會被 TCP 檢測出,當咱們從新插回網線,仍舊能夠正常使用 ssh,同時此時並無發生任何 TCP 的重連。
有人會說 TCP 不是有 KeepAlive 機制麼,經過這個機制來實現不就能夠了嗎?可是事實上,TCP KeepAlive 的機制其實並不適用於此。Keep Alive 機制開啓後,TCP 層將在定時時間到後發送相應的 KeepAlive 探針以肯定鏈接可用性。通常時間爲 7200 s,失敗後重試 10 次,每次超時時間 75 s。顯然默認值沒法知足咱們的需求,而修改過設置後就能夠知足了嗎?答案仍舊是否認的。由於 TCP KeepAlive 是用於檢測鏈接的死活,而心跳機制則附帶一個額外的功能:檢測通信雙方的存活狀態。二者聽起來彷佛是一個意思,但實際上卻截然不同。考慮一種狀況,某臺服務器由於某些緣由致使負載超高,CPU 100%,沒法響應任何業務請求,可是使用 TCP 探針則仍舊可以肯定鏈接狀態,這就是典型的鏈接活着但業務提供方已死的狀態,對客戶端而言,這時的最好選擇就是斷線後從新鏈接其餘服務器,而不是一直認爲當前服務器是可用狀態,一直向當前服務器發送些必然會失敗的請求。
從上面咱們能夠知道,KeepAlive 並不適用於檢測雙方存活的場景,這種場景還得依賴於應用層的心跳。應用層心跳有着更大的靈活性,能夠控制檢測時機,間隔和處理流程,甚至能夠在心跳包上附帶額外信息。從這個角度而言,應用層的心跳的確是最佳實踐。算法

  • 如何實現心跳指令?

從上面咱們能夠得出結論,目前而言,應用層心跳的確是檢測鏈接有效性,雙方是否存活的最佳實踐,那麼剩下的問題就是怎麼實現。
最簡單粗暴作法固然是定時心跳,如每隔 30 秒心跳一次,15 秒內沒有收到心跳回包則認爲當前鏈接已失效,斷開鏈接並進行重連。這種作法最直接,實現也簡單。惟一的問題是比較耗電和耗流量。以一個協議包 5 個字節計算,一天收發 2880 個心跳包,一個月就是 5 2 2880 * 30 = 0.8 M 的流量,若是手機上多裝幾個 IM 軟件,每月光心跳就好幾兆流量沒了,更不用說頻繁的心跳帶來的電量損耗。
既然頻繁心跳會帶來耗電和耗流量的弊端,改進的方向天然是減小心跳頻率,但也不能過於影響鏈接檢測的實時性。基於這個需求,通常能夠將心跳間隔根據程序狀態進行調整,當程序在後臺時(這裏主要考慮安卓),儘可能拉長心跳間隔,5 分鐘,甚至 10 分鐘均可以。而當 App 在前臺時則按照原來規則操做。鏈接可靠性的判斷也能夠放寬,避免一次心跳超時就認爲鏈接無效的狀況,使用錯誤積累,只在心跳超時 n 次後才斷定當前鏈接不可用。固然還有一些小 trick 好比從收到的最後一個指令包進行心跳包週期計時而不是固定時間,這樣也可以必定程度減小心跳次數。服務器

以上就是網易雲信對於心跳指令的理解和實踐,《移動IM開發指南》第三篇文章將爲你們介紹如何優化登陸模塊,敬請期待。微信


隨着即時通信以及音頻處理和壓縮技術的不斷髮展,效果更好、適用範圍更廣、性能更高的算法和新的技術必將不斷涌現,若是你有好的技術或者分享,歡迎關注網易雲信官方博客和 GitHub:網絡

關注更多技術乾貨內容: 網易雲信博客
歡迎關注 網易雲信 GitHub
歡迎關注 網易雲信官網
相關文章
相關標籤/搜索