项望烽:移动 IM 开发之心跳
作者:項望烽
網(wǎng)易杭州研究院·網(wǎng)易云信 iOS端負責人
緣起
做云信(netease.im)一年多以來,每隔兩星期會兼職一次技術支持,回答客戶的各種問題。也經(jīng)常會碰到些較真的技術人員詢問技術細節(jié),如:云信如何做心跳,如何保證消息必達,如何加快文件上傳等。
因為接客量巨大(臣妾忙不過來啊),往往只能簡略介紹,并不能具體展開。于是,決定寫成“移動 IM 問題詳解系列”。
移動 IM 開發(fā)之心跳
WHAT
在使用 TCP 長連接的 IM 服務設計中,往往都會涉及到心跳。心跳一般是指某端(絕大多數(shù)情況下是客戶端)每隔一定時間向對端發(fā)送自定義指令,以判斷雙方是否存活,因其按照一定間隔發(fā)送,類似于心跳,故被稱為心跳指令。
WHY
那么問題就隨之而來了:為什么需要在應用層做心跳,難道 TCP 不是個可靠連接嗎?我們不能夠依賴 TCP 做斷線檢測嗎?比如使用 TCP 的 KeepAlive 機制來實現(xiàn)。應用層心跳是目前的最佳實踐嗎?怎么樣的心跳才是最佳實踐。
好吧,是不是以前從來沒有仔細考慮過這些問題,僅僅只是個簡單的心跳而已啊!(一臉懵逼)
對于客戶端而言,使用 TCP 長連接來實現(xiàn)業(yè)務的最大驅動力在于:在當前連接可用的情況下,每一次請求都只是簡單的數(shù)據(jù)發(fā)送和接受,免去了 DNS 解析,連接建立等時間,大大加快了請求的速度,同時也有利于接受服務器的實時消息。
但前提是連接可用。如果連接無法很好地保持,每次請求就會變成撞大運:運氣好,通過長連接發(fā)送請求并收到反饋。運氣差,當前連接已失效,請求遲遲沒有收到反饋直到超時,又需要一次連接建立的過程,其效率甚至還不如 HTTP。而連接保持的前提必然是檢測連接的可用性,并在連接不可用時主動放棄當前連接并建立新的連接。基于這個前提,必須要有一種機制用于檢測連接可用性。同時移動網(wǎng)絡的特殊性也要求客戶端需要在空余時間發(fā)送一定的信令,避免連接被回收。詳見《微信和運營商的撕B》http://www.zhihu.com/question/20849677。
而對于服務器而言,能夠及時獲悉連接可用性也非常重要:一方面服務器需要及時清理無效連接以減輕負載,另一方面也是業(yè)務的需求,如游戲副本中服務器需要及時處理玩家掉線帶來的問題。
上面說了保持連接的重要性,那么現(xiàn)在回到具體實現(xiàn)上。為什么我們需要使用應用層心跳來做檢測,而不是直接使用 TCP 的特性呢?
我們知道 TCP 是一個基于連接的協(xié)議,其連接狀態(tài)是由一個狀態(tài)機進行維護,連接完畢后,雙方都會處于 established 狀態(tài),這之后的狀態(tài)并不會主動進行變化。這意味著如果上層不進行任何調用,一直使 TCP 連接空閑,那么這個連接雖然沒有任何數(shù)據(jù),但仍是保持連接狀態(tài),一天,一星期,甚至一個月,即使在這期間中間路由崩潰重啟無數(shù)次。舉個現(xiàn)實中經(jīng)常遇到的栗子:當我們 ssh 到自己的 VPS 上,然后不小心踢掉網(wǎng)線,此時的網(wǎng)絡變化并不會被 TCP 檢測出,當我們重新插回網(wǎng)線,仍舊可以正常使用 ssh,同時此時并沒有發(fā)生任何 TCP 的重連。
有人會說 TCP 不是有 KeepAlive 機制么,通過這個機制來實現(xiàn)不就可以了嗎?但是事實上,TCP KeepAlive 的機制其實并不適用于此。Keep Alive 機制開啟后,TCP 層將在定時時間到后發(fā)送相應的 KeepAlive 探針以確定連接可用性。一般時間為 7200 s,失敗后重試 10 次,每次超時時間 75 s。顯然默認值無法滿足我們的需求,而修改過設置后就可以滿足了嗎?答案仍舊是否定的。因為 TCP KeepAlive 是用于檢測連接的死活,而心跳機制則附帶一個額外的功能:檢測通訊雙方的存活狀態(tài)。兩者聽起來似乎是一個意思,但實際上卻大相徑庭。考慮一種情況,某臺服務器因為某些原因導致負載超高,CPU 100%,無法響應任何業(yè)務請求,但是使用 TCP 探針則仍舊能夠確定連接狀態(tài),這就是典型的連接活著但業(yè)務提供方已死的狀態(tài),對客戶端而言,這時的最好選擇就是斷線后重新連接其他服務器,而不是一直認為當前服務器是可用狀態(tài),一直向當前服務器發(fā)送些必然會失敗的請求。
從上面我們可以知道,KeepAlive 并不適用于檢測雙方存活的場景,這種場景還得依賴于應用層的心跳。應用層心跳有著更大的靈活性,可以控制檢測時機,間隔和處理流程,甚至可以在心跳包上附帶額外信息。從這個角度而言,應用層的心跳的確是最佳實踐。
HOW
從上面我們可以得出結論,目前而言,應用層心跳的確是檢測連接有效性,雙方是否存活的最佳實踐,那么剩下的問題就是怎么實現(xiàn)。
最簡單粗暴做法當然是定時心跳,如每隔 30 秒心跳一次,15 秒內沒有收到心跳回包則認為當前連接已失效,斷開連接并進行重連。這種做法最直接,實現(xiàn)也簡單。唯一的問題是比較耗電和耗流量。以一個協(xié)議包 5 個字節(jié)計算,一天收發(fā) 2880 個心跳包,一個月就是 5 * 2 * 2880 * 30 = 0.8 M 的流量,如果手機上多裝幾個 IM 軟件,每個月光心跳就好幾兆流量沒了,更不用說頻繁的心跳帶來的電量損耗。
既然頻繁心跳會帶來耗電和耗流量的弊端,改進的方向自然是減少心跳頻率,但也不能過于影響連接檢測的實時性。基于這個需求,一般可以將心跳間隔根據(jù)程序狀態(tài)進行調整,當程序在后臺時(這里主要考慮安卓),盡量拉長心跳間隔,5 分鐘,甚至 10 分鐘都可以。而當 App 在前臺時則按照原來規(guī)則操作。連接可靠性的判斷也可以放寬,避免一次心跳超時就認為連接無效的情況,使用錯誤積累,只在心跳超時 n 次后才判定當前連接不可用。當然還有一些小 trick 比如從收到的最后一個指令包進行心跳包周期計時而不是固定時間,這樣也能夠一定程度減少心跳次數(shù)。
·?END?·
【下期預告】
《移動 IM 開發(fā)之登錄優(yōu)化》
【精彩回顧】
項望烽:移動IM開發(fā)那些事兒
項望烽:iOS App開發(fā)那些事兒
網(wǎng)易云信|真正穩(wěn)定的IM云服務
http://netease.im ?長按關注,干貨不斷
總結
以上是生活随笔為你收集整理的项望烽:移动 IM 开发之心跳的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云信“欢乐颂活动”中奖名单
- 下一篇: 项望烽:移动 IM 开发之登录优化