IM系統功能-心跳機制(及解決方案)

爲什麼需要心跳機制

“長連接”方式給我們帶來了衆多好處,那麼要讓消息通過“長連接”實現可靠投遞,最重要的環節就在於如何維護好這個“長連接”。由於這個“長連接”底層使用的 TCP 連接並不是一個真正存在的物理連接,實際上只是一個無感知的虛擬連接,中間鏈路的斷開連接的兩端不會感知到,因此維護好這個“長連接”一個關鍵的問題在於能夠讓這個“長連接”能夠在中間鏈路出現問題時,讓連接的兩端能快速得到通知,然後通過“重連”來重新建立新的可用連接,從而讓我們這個“長連接”一直保持“高可用”狀態。 

這個能夠“快速”“不間斷”識別連接可用性的機制,被稱爲“心跳機制”。“心跳機制”通過持續的往連接上發送“模擬數據”來試探連接的可用性,同時也讓我們的連接在沒有真正業務數據收發的時候,也持續有數據流通,而不會被中間的網絡運營商以爲連接已經沒有在使用而把連接切斷。

1.降低服務端連接維護的開銷

對於大部分即時通訊場景,消息收發雙方經常處於移動網絡環境中,手機信號強弱變化及中間路由故障等,都可能導致“長連接”實際處於不可用狀態。

比如:用戶拿着手機進電梯了,手機網絡信號忽然完全沒了,長連接此時已經不可用,但 IM 服務端無法感知到這個“連接不可用”的情況;另外,假如我們上網的路由器忽然掉線了,之前 App 和 IM 服務端建立的長連接,此時實際也處於不可用狀態,但是客戶端和 IM 服務器也都無法感知。之所以能夠實現消息的“服務端推送”,是因爲我們針對每一臺上線的設備,都會在 IM 服務端維護相應的“用戶設備”和“網絡連接”這麼一個映射關係,除此之外,很多時候爲了節省網絡開銷,還會在服務端臨時緩存一些沒必要每次請求都攜帶的客戶端的信息(比如:app 版本號、操作系統、網絡狀態等),這樣客戶端一旦建好長連後,只需要首次攜帶這些信息,後續請求可以不用再攜帶,而是使用 IM 服務端緩存的這些信息。另外,在很多 IM 的實現上,還會在服務端維護一些“用戶在線狀態”和“所有在線設備”這些信息,便於業務使用。

如果 IM 服務端無法感知到這些連接的異常情況,會導致的一個問題是:IM 服務端可能維護了大量“無效的連接”,從而導致嚴重的連接句柄的資源浪費;同時也會緩存了大量實際上已經沒有用了的“映射關係”“設備信息”“在線狀態”等信息,也是對資源的浪費;另外,IM 服務端在往“無效長連接”推送消息,以及後續的重試推送都會降低服務的整體性能。

2.支持客戶端斷線重連

通過“心跳”快速識別連接的可用性,除了可以降低服務端的資源開銷,也被用於來支撐客戶端的斷開重連機制。對於客戶端發出心跳包,如果在一定的超時時間內(考慮到網絡傳輸具有一定的延遲性,這個超時時間至少要大於一個心跳的間隔),比如連續兩次發送心跳包,都沒有收到 IM 服務端的響應,那麼客戶端可以認爲和服務端的長連接不可用,這時客戶端可以斷線重連。導致服務端沒有響應的原因可能是和服務端的網絡在中間環節被斷開,也可能是服務器負載過高無法響應心跳包,不管什麼情況,這種場景下斷線重連是很有必要的,它能夠讓客戶端快速自動維護連接的可用性。

3.連接保活

維護一條“高可用”的長連接,還有一個重要的任務就是儘量讓建立的長連接存活時間更長。

這裏你可能會問:難道在用戶網絡和中間路由網絡都正常的情況下,長連接還可能會被殺死?答案是:確實會。

探究這個原因的話,我可能要從 IPv4 說起。由於 IPv4 的公網 IP 的資源有限性(約 43 億個),爲了節省公網 IP 的使用,通過移動運營商上網的手機實際上只是分配了一個運營商內網的 IP。

在訪問 Internet 時,運營商網關通過一個“外網 IP 和端口”到“內網 IP 和端口”的雙向映射表,來讓實際使用內網 IP 的手機能和外網互通,這個網絡地址的轉換過程叫做 NAT(Network Address Translation)。

NAT 本身的實現機制並沒有什麼不妥,問題在於很多運營商爲了節省資源和降低自身網關的壓力,對於一段時間沒有數據收發的連接,運營商會將它們從 NAT 映射表中清除掉,而且這個清除動作也不會被手機端和 IM 服務端感知到。這樣的話,如果沒有了 NAT 映射關係,長連接上的消息收發都無法正常進行。而且多長時間會從 NAT 映射表清除,每個地方的運營商也是不盡相同,從幾分鐘到幾小時都有。假設用戶有幾分鐘沒有收發消息,可能這個長連接就已經處於不可用狀態了。那麼,如果我們的客戶端能在沒有消息收發的空閒時間給服務端發送一些信令,就能避免長連接被運營商 NAT 幹掉了,這些“信令”一般就是通過心跳包來實現。

心跳檢測的幾種實現方式

目前業界有三種常用的實現方法:TCP Keepalive、應用層心跳及智能心跳。

1.TCP Keepalive

TCP 的 Keepalive 作爲操作系統的 TCP/IP 協議棧實現的一部分,對於本機的 TCP 連接,會在連接空閒期按一定的頻次,自動發送不攜帶數據的探測報文,來探測對方是否存活。操作系統默認是關閉這個特性的,需要由應用層來開啓。默認的三個配置項:心跳週期是 2 小時,失敗後再重試 9 次,超時時間 75s。三個配置項均可以調整。

這樣來看,TCP 的 Keepalive 作爲系統層 TCP/IP 協議棧的已有實現,不需要其他開發工作量,用來作爲連接存活與否的探測機制是非常方便的;上層應用只需要處理探測後的連接異常情況即可,而且心跳包不攜帶數據,帶寬資源的浪費也是最少的。由於易用性好、網絡消耗小等優勢,TCP Keepalive 在很多 IM 系統中被開啓使用,之前抓包就發現,WhatsApps 使用空閒期 10 秒間隔的 TCP Keepalive 來進行存活探測。雖然擁有衆多優勢,但 TCP Keepalive 本身還是存在一些缺陷的,比如心跳間隔靈活性較差,一臺服務器某一時間只能調整爲固定間隔的心跳;另外 TCP Keepalive 雖然能夠用於連接層存活的探測,但並不代表真正的應用層處於可用狀態。

缺點:我舉一個例子,比如 IM 系統出現代碼死鎖、阻塞的情況下,實際上已經無法處理業務請求了,但此時連接層 TCP Keepalive 的探針不需要應用層參與,仍然能夠在內核層正常響應。這種情況就會導致探測的誤判,讓已失去業務處理能力的機器不能被及時發現。

2.應用層心跳

爲了解決 TCP Keepalive 存在的一些不足的問題,很多 IM 服務使用應用層心跳來提升探測的靈活性和準確性。應用層心跳實際上就是客戶端每隔一定時間間隔,向 IM 服務端發送一個業務層的數據包告知自身存活。如果 IM 服務端在一定時間內沒有收到心跳包,就認定客戶端由於某種原因連接不可達了,此時就會從 IM 服務端把這個連接斷開,同時清除相應分配的其他資源。

應用層心跳和 TCP Keepalive 心跳相比,由於不屬於 TCP/IP 協議棧的實現,因此會有一些額外的數據傳輸開銷,但是大部分應用層心跳的設計上心跳包都儘量精簡,一般就幾個字節,比如有些應用層心跳包只是一個空包用於保活,有的心跳包只是攜帶了心跳間隔,用於客戶端調整下一次的心跳,所以額外的數據開銷都非常小。應用層心跳相比 TCP Keepalive,由於需要在應用層進行發送和接收的處理,因此更能反映應用的可用性,而不是僅僅代表網絡可用。而且應用層心跳可以根據實際網絡的情況,來靈活設置心跳間隔,對於國內運營商 NAT 超時混亂的實際情況下,靈活可設置的心跳間隔在節省網絡流量和保活層面優勢更明顯。

目前大部分 IM 都採用了應用層心跳方案來解決連接保活和可用性探測的問題。比如之前抓包中發現 WhatApps 的應用層心跳間隔有 30 秒和 1 分鐘,微信的應用層心跳間隔大部分情況是 4 分半鐘,目前微博長連接採用的是 2 分鐘的心跳間隔。每種 IM 客戶端發送心跳策略也都不一樣,最簡單的就是按照固定頻率發送心跳包,不管連接是否處於空閒狀態。之前抓手機 QQ 的包,就發現 App 大概按照 45s 的頻率固定發心跳;還有稍微複雜的策略是客戶端在發送數據空閒後才發送心跳包,這種相比較對流量節省更好,但實現上略微複雜一些。

下面是一個典型的應用層心跳的客戶端和服務端的處理流程圖,從圖中可以看出客戶端和服務端,各自通過心跳機制來實現“斷線重連”和“資源清理”。

需要注意的是:對於客戶端來說,判斷連接是否空閒的時間是既定的心跳間隔時間,而對於服務端來說,考慮到網絡數據傳輸有一定的延遲,因此判斷連接是否空閒的超時時間需要大於心跳間隔時間,這樣能避免由於網絡傳輸延遲導致連接可用性的誤判。智能心跳

3.智能心跳

在國內移動網絡場景下,各個地方運營商在不同的網絡類型下 NAT 超時的時間差異性很大。採用固定頻率的應用層心跳在實現上雖然相對較爲簡單,但爲了避免 NAT 超時,只能將心跳間隔設置爲小於所有網絡環境下 NAT 超時的最短時間,雖然也能解決問題,但對於設備 CPU、電量、網絡流量的資源無法做到最大程度的節約。爲了優化這個現象,很多即時通訊場景會採用“智能心跳”的方案,來平衡“NAT 超時”和“設備資源節約”。所謂智能心跳,就是讓心跳間隔能夠根據網絡環境來自動調整,通過不斷自動調整心跳間隔的方式,逐步逼近 NAT 超時臨界點,在保證 NAT 不超時的情況下儘量節約設備資源。據說微信就採用了智能心跳方案來優化心跳間隔。不過從個人角度看,隨着目前移動資費的大幅降低,手機端硬件設備條件也越來越好,智能心跳對於設備資源的節約效果有限。而且智能心跳方案在確認 NAT 超時臨界點的過程中,需要不斷嘗試,可能也會從一定程度上降低“超時確認階段”連接的可用性,因此,我建議你可以根據自身業務場景的需要,來權衡必要性。

總結:

通過客戶端和 IM 服務端建立的“心跳機制”可以快速自動識別連接是否可用,同時避免運營商 NAT 超時被斷開的情況。“心跳機制”解決了以下三方面的問題:

1.降低服務端連接維護無效連接的開銷。

2.支持客戶端快速識別無效連接,自動斷線重連。

3.連接保活,避免被運營商 NAT 超時斷開。

心跳探測的實現業界大部分綜合採用以下兩種方式:

1.TCP Keepalive。操作系統 TCP/IP 協議棧自帶,無需二次開發,使用簡單,不攜帶數據網絡流量消耗少。但存在靈活性不夠和無法判斷應用層是否可用的缺陷。

2.應用層心跳。應用自己實現心跳機制,需要一定的代碼開發量,網絡流量消耗稍微多一點,但心跳間隔的靈活性好,配合智能心跳機制,可以做到”保證 NAT 不超時的情況下最大化節約設備資源消耗“,同時也能更精確反饋應用層的真實可用性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章