1. 程式人生 > >長連接及心跳保活原理簡介

長連接及心跳保活原理簡介

圖形 相對 動作 https 初始 檢測 sin 過程 tsv

  本文簡要的分析了長連接產生的背景以及所解決的問題,並對比了keep-alive與心跳機制對長連接保活的影響,最後詳細的介紹了心跳保活的兩個關鍵因素–DHCP協議與NAT原理。如有不當之處,歡迎批評和指正。


1.短連接,並行連接,持久連接與長連接

(1) 短連接簡介

  在互聯網發展過程中,最為普及的應用就是HTTP超文本傳輸協議,而在早期–HTTP1.0的協議都是建立在TCP協議基礎上,其特點就是傳輸完數據後,立馬就釋放掉該TCP鏈接,所以就有了形象的短連接這個稱號。下圖形象的展示出了在一個事務的處理過程中,各個階段的處理時長:

技術分享圖片

  可以看到,與建立TCP連接,以及傳輸請求和響應報文的時間相比,事務處理時間可能是很短的。短連接的性能瓶頸主要集中在如下幾個方面:

a.TCP連接的握手時延

技術分享圖片

  在發送數據之前,TCP要傳送兩個分組來建立連接(現代的TCP棧都允許客戶端在確認分組中發送數據),此時,SYN/SYN+ACK握手會產生一個可測量的時延。

b.延遲確認

  每個TCP段都有一個序列號和數據完整性校驗和。每個段的接收者收到完好的段時,都會向發送者回送小的確認分組。如果發送者沒有在指定的窗口時間內收到確認信息,發送者就認為分組已被破壞或損毀,並重發數據。

  由於確認報文很小,所以TCP允許在發往相同方向的輸出數據分組中對其進行“捎帶”。TCP將返回的確認信息與輸出的數據分組結合在一起,可以更有效地利用網絡。為了增加確認報文找到同向傳輸數據分組的可能性,很多TCP棧都實現了一種“延遲確認”算法。延遲確認算法會在一個特定的窗口時間(通常是100~200毫秒)內將輸出確認存放在緩沖區中,以尋找能夠捎帶它的輸出數據分組。如果在那個時間段內沒有輸出數據分組,就將確認信息放在單獨的分組中傳送。

c.TCP慢啟動

  TCP連接會隨著時間進行自我“調諧”,起初會限制連接的最大速度,如果數據成功傳輸,會隨著時間的推移提高傳輸的速度,這種調諧被稱為TCP慢啟動,用於防止因特網的突然過載和擁塞。

  由於存在這種擁塞控制特性,所以新連接的傳輸速度會比已經交換過一定量數據的、“已調諧”連接慢一些。

(2) 短連接的適用場景與優缺點

  短連接多用於操作頻繁,點對點的通訊,而且連接數不能太多的情況。每個TCP連接的建立都需要三次握手,每個TCP連接的斷開要四次揮手。適用於並發量大,但是每個用戶又不需頻繁操作的情況。
  但是在用戶需要頻繁操作的業務場景下(如新用戶註冊,網購提交訂單等),頻繁的使用短連接則會使性能時延產生疊加,如下如:

技術分享圖片

  因此就產生了一些列關於連接性能的改進方案。

(3) 並行連接

  並行連接允許客戶端打開多條連接,並行地執行多個事務,每個事務都有自己的TCP連接。這樣可以克服單條連接的空載時間和帶寬限制,時延可以重疊起來,而且如果單條連接沒有充分利用客戶端的網絡帶寬,可以將未用帶寬分配來裝載其他對象。
  在PC時代,利用並行連接來充分利用現代瀏覽器的多線程並發下載能力的場景非常廣泛。
  但是並行連接也會產生一定的問題,首先並行連接不一定更快,因為帶寬資源有限,每個連接都會去競爭這有限的帶寬,這樣帶來的性能提升就很小,甚至沒什麽提升。另外打開大量連接會消耗很多內存資源,從而引發自身的性能問題,因此每個瀏覽器,允許對每個域名的連接數一般是有上限的,如下圖所示:

技術分享圖片

(4) 持久連接

  HTTP1.0版本以後,允許HTTP設備在事務處理結束之後將TCP連接保持在打開狀態,以便為未來的HTTP請求重用現存的連接。在事務處理結束之後仍然保持在打開狀態的TCP連接被稱為持久連接。非持久連接會在每個事務結束之後關閉。持久連接會在不同事務之間保持打開狀態,直到客戶端或服務器決定將其關閉為止。
  現在很多方案都會采用持久連接+新連接結合的方式,這種方式盡可能的減少了新建連接的浪費,同時當現有連接沒有辦法滿足需求的時候,可以建立新連接滿足需求,比較靈活。
  持久連接的時間參數,通常由服務器設定,比如nginx的keepalivetimeout,keepalive timout時間值意味著:一個http產生的tcp連接在傳送完最後一個響應後,還需要hold住keepalive_timeout秒後,才開始關閉這個連接;

  持久連接與並行連接相比,帶來的優勢如下:

  • 避免了每個事務都會打開/關閉一條新的連接,造成時間和帶寬的耗費;
  • 避免了TCP慢啟動特性的存在導致的每條新連接的性能降低;
  • 可打開的並行連接數量實際上是有限的,持久連接則可以減少建立的連接的數量;

(5) 長連接

  長連接與持久連接本質上非常的相似,持久連接側重於HTTP應用層,特指一次請求結束之後,服務器會在自己設置的keepalivetimeout時間到期後才關閉已經建立的連接。長連接則是client方與server方先建立連接,連接建立後不斷開,然後再進行報文發送和接收,直到有一方主動關閉連接為止。

  長連接的適用場景也非常的廣泛:

  • 監控系統:後臺硬件熱插拔、LED、溫度、電壓發生變化等;
  • IM應用:收發消息的操作;
  • 即時報價系統:例如股市行情push等;
  • 推送服務:各種App內置的push提醒服務;

  像以上這些連接,如果每次操作都要建立連接然後再操作的話處理速度會降低,並且時效性也不高。通過長連接,第一次連接上以後每次直接發送數據就可以了,不用再建立TCP連接。


2.長連接保活,Keep-Alive與心跳保活技術

(1) 為何需要長連接保活

  上一節的分析可以看到,對於客戶端而言,使用TCP長連接來實現業務的好處在於:在當前連接可用的情況下,每一次請求都只是簡單的數據發送和接受,免去了DNS解析,連接建立,TCP慢啟動等時間,大大加快了請求的速度,同時也有利於接收服務器的實時消息。
  在使用TCP長連接的業務場景下,保持長連接的可用性非常重要。如果長連接無法很好地保持,在連接已經失效的情況下繼續發送請求會導致遲遲收不到響應直到超時,又需要一次連接建立的過程,其效率甚至還不如直接使用短連接。而連接保持的前提必然是檢測連接的可用性,並在連接不可用時主動放棄當前連接並建立新的連接。

(2) 心跳保活

  App實現長連接保活的方式通常是采用應用層心跳,通過心跳包的超時和其他條件(網絡切換)來執行重連操作。心跳一般是指某端(絕大多數情況下是客戶端)每隔一定時間向對端發送自定義指令,以判斷雙方是否存活,因其按照一定間隔發送,類似於心跳,故被稱為心跳指令。

(3) Keep-Alive可否實現保活?

a.HTTP中的Keep-Alive

  實現HTTP/1.0 keep-alive連接的客戶端可以通過包含Connection:Keep-Alive首部請求將一條連接保持在打開狀態,如果服務器願意為下一條請求將連接保持在打開狀態,就在響應中包含相同的首部。如果響應中沒有Connection: Keep-Alive首部,客戶端就認為服務器不支持keep-alive,會在發回響應報文之後關閉連接。HTTP/1.1以後Keep-Alive是默認打開的。

c.TCP中的Keep-Alive

  TCP協議的實現中,提供了KeepAlive報文,用來探測連接的對端是否存活。在應用交互的過程中,可能存在以下幾種情況:

  • 客戶端或服務器意外斷電,死機,崩潰,重啟;
  • 中間網絡已經中斷,而客戶端與服務器並不知道;

  利用保活探測功能,可以探知這種對端的意外情況,從而保證在意外發生時,可以釋放半打開的TCP連接。TCP保活報文交互過程如下:

技術分享圖片

  雖然TCP提供了KeepAlive機制,但是並不能替代應用層心跳保活。原因主要如下:

  • (1) Keep Alive機制開啟後,TCP層將在定時時間到後發送相應的KeepAlive探針以確定連接可用性。默認時間為7200s(兩小時),失敗後重試10次,每次超時時間75s。顯然默認值無法滿足移動網絡下的需求;
  • (2) 即便修改了(1)中的默認值,也不能很好的滿足業務需求。TCP的KeepAlive用於檢測連接的死活而不能檢測通訊雙方的存活狀態。比如某臺服務器因為某些原因導致負載超高,無法響應任何業務請求,但是使用TCP探針則仍舊能夠確定連接狀態,這就是典型的連接活著但業務提供方已死的狀態,對客戶端而言,這時的最好選擇就是斷線後重新連接其他服務器,而不是一直認為當前服務器是可用狀態,一直向當前服務器發送些必然會失敗的請求。
  • (3) socks代理會讓Keep Alive失效。socks協議只管轉發TCP層具體的數據包,而不會轉發TCP協議內的實現細節的包。所以,一個應用如果使用了socks代理,那麽TCP的KeepAlive機制就失效了。
  • (4) 部分復雜情況下Keep Alive會失效,如路由器掛掉,網線直接被拔除等;

  因此,KeepAlive並不適用於檢測雙方存活的場景,這種場景還得依賴於應用層的心跳。應用層心跳也具備著更大的靈活性,可以控制檢測時機,間隔和處理流程,甚至可以在心跳包上附帶額外信息。

(4) 影響心跳頻率的關鍵因素

  通過上一節的分析可以看到應用層心跳是檢測連接有效性以及判斷雙方是否存活的有效方式。但是心跳過於頻繁會帶來耗電和耗流量的弊病,心跳頻率過低則會影響連接檢測的實時性。業內關於心跳時間的設置和優化,主要基於如下幾個因素:

  • 1.NAT超時–大部分移動無線網絡運營商在鏈路一段時間沒有數據通訊時,會淘汰 NAT表中的對應項,造成鏈路中斷;
  • 2.DHCP租期–DHCP租期到了需要主動續約,否則會繼續使用過期IP導致長連接偶然的斷連;
  • 3.網絡狀態變化–手機網絡和WIFI網絡切換、網絡斷開和連上等情況有網絡狀態的變化,也會使長連接變為無效連接;

  網絡狀態變化導致長連接變為無效連接的原因很容易理解。但是NAT超時和DHCP租期的問題對長連接保活存在的影響就涉及到網絡協議底層的細節了。後續會對這兩個原理進行相應的分析。


3.DHCP原理淺析及其對心跳保活的影響

(1) DHCP協議簡介

  DHCP協議全稱為Dynamic Host Configuration Protocol– 動態主機配置協議,主要用於在一個局域網裏為主機動態的分配IP地址。DHCP有三種分配IP地址方式:

  • 自動分配:DHCP給客戶端分配永久性的IP地址;
  • 動態分配:DHCP給客戶端分配的IP地址過一段時間後會過期,或者客戶端可以主動釋放該地址(最常用的方式);
  • 手動配置:由用戶手動為客戶端指定IP地址;

(2) DHCP工作流程詳解

  DHCP協議為客戶端分配IP的過程大致如下:

技術分享圖片

1.DHCP Discover

  DHCP客戶端(需要上網的設備)以廣播(因為客戶端還不知道DHCP服務器的IP地址)的方式發送DHCP Discover包,來尋找DHCP服務器,即向地址255.255.255.255發送特定的廣播信息。網絡上每一臺安裝了TCP/IP協議的主機都會收到該廣播消息,但只有DHCP服務器才會做出響應。

技術分享圖片

技術分享圖片

2.DHCP Offer

  在該階段,DHCP服務器提供IP地址。在網絡中接收到DHCP Discover包的DHCP服務器,都會做出響應。這些DHCP服務器從尚未出租的IP地址中挑選一個給客戶端,向客戶端發送一個包含IP地址和其他設置的DHCP Offer包。

技術分享圖片

技術分享圖片

3.DHCP Request

技術分享圖片
  該階段需要DHCP客戶端選擇某臺DHCP服務器提供的IP地址,如上圖所示,可以看到3臺DHCP服務器都向客戶端發送了DHCP Offer,此時,DHCP客戶端只能接受第一個收到的DHCP Offer包信息。然後,以廣播的方式回答一個DHCP Request請求信息,該信息中包含它所選定的DHCP服務器請求IP地址的內容。

技術分享圖片

4.DHCP ACK

  確認階段,DHCP服務器確認所提供的的IP地址階段,告訴DHCP客戶端可以使用它所提供的IP地址。

技術分享圖片

(3) DHCP的續租問題

  在DHCP ACK報文中,有3個關於續租時間相關的字段:

  • Lease Time:
    IP地址租約時間,超過了這個時間後,IP地址被DHCP服務器收回;
  • Renewal Time:
    默認為Lease Time的1/2,表示客戶端需要進行續約的時間。客戶端發送一個DHCP REQUEST消息給原始的DHCP服務器,並等待回復。DHCP服務器返回DHCP ACK則表示同意續期,客戶端更新自己的Renewal Time與Rebinding Time即可。
  • Rebinding Time:
    默認為Lease Time的7/8,客戶端在續期失敗的情況下,Rebinding Time到期時,會向局域網內廣播發送一條DHCP REQUEST消息,如果還沒有DHCP服務器響應直至租約Lease Time到期,將恢復到初始狀態。

  DHCP完成的狀態變遷流程如下:

技術分享圖片

(4) DHCP租期問題對心跳保活的影響

  在設計心跳頻率時,DHCP租期是一個不確定因素,但是原則是心跳的最大間隔應該低於DHCP的租期時間。
  另外,在Android的一些版本上,存在DHCP租期到了不會主動續約並且會繼續使用過期IP的bug。這個問題導致的問題表象是,在超過租期的某個時間點(沒有規律)會導致IP過期,老的TCP連接不能正常收發數據。並且系統沒有網絡變化事件,只有等應用判斷主動建立新的TCP連接才引起安卓設備重新向DHCP Server申請IP租用。詳情可見–Android 2.1 - 4.1.1 Allows DHCP Lease to Expire, Keeps Using IP Address。


4.NAT原理淺析及其對心跳保活的影響

(1) NAT技術產生的背景

  在網絡協議制定的初期設計網絡地址的時候,32bits位長即2的32次冪臺終端設備連入互聯網已經是一個非常大的數量了,再加上增加ip的長度(即使是從4字節增到6字節)對當時設備的計算、存儲、傳輸成本也是相當巨大的。因此IP地址設計為了32位,並且在早期所有需要上網的設備都有自己的IP地址,也就是說那個時候沒有內網和外網的區別,所有客戶端都是直接連接到互聯網的。
  進入20世紀90年代之後,互聯網逐步向公眾普及,接入互聯網的設備數量也快速增長,如果還用原來的方法接入,過不了多久,可分配的地址就用光了。如果不能保證每臺設備有唯一不重復的地址,就會從根本上影響網絡包的傳輸,這是一個非常嚴重的問題。如果任由這樣發展下去,不久的將來,一旦固定地址用光,新的設備就無法接入了。在這個背景下NAT技術誕生了(雖然ipv6也是解決辦法,但始終普及不開來,而且未來到底ipv6夠不夠用仍是未知)。

(2) NAT技術的基本工作原理

a.NAT技術的本質

  NAT技術主要是為了解決公網IP地址不足的問題,所以才會采取這種地址轉換的策略。本質上就是讓一群機器公用同一個IP,這樣就暫時解決了IP短缺的問題。

b.私有地址與公有地址

  不同內網之間是完全獨立的。內網之間不會有網絡包流動,即使內網A的某臺服務器和內網B的某臺客戶端具有相同的IP地址也沒關系,因為它們之間不會進行通信。只要在每個內網自己的範圍內,能夠明確判斷網絡包的目的地就可以了,是否和其他局域網中的內網地址重復無關緊要,只要每個局域網自己的網絡是相互獨立的,
就不會出現問題。
  解決地址不足的問題,利用的就是這樣的原理,即局域網的內部設備的地址不一定要和其他局域網中的內部設備地址不重復。這樣一來,局域網的內部設備就不需要分配固定地址了,從而大幅節省了IP地址。內部設備分配IP地址的方式,就是通過上一節的DHCP協議進行。內網地址的分配有相應的規則,規定某些地址是用於內網的,這些地址叫作私有地址,而原來的固定地址則叫作公有地址。
  在內網中可用作私有地址的範圍僅限以下這些:

  • 10.0.0.0~10.255.255.255
  • 172.16.0.0~172.31.255.255
  • 192.168.0.0~192.168.255.255

  在制定私有地址規則時,這些地址屬於公有地址中還沒有分配的範圍。換句話說,私有地址本身並沒有什麽特別的結構,只不過是將公有地址中沒分配的一部分拿出來規定只能在內網使用它們而已。

c.地址轉換(NAT)機制的加入

  當內網和互聯網之間需要傳輸包的時候,問題就出現了,因為如果很多地方都出現相同的地址,包就無法正確傳輸了。因此當公司內網和互聯網連接的時候,需要采用下圖這樣的結構,即將公司內網分成兩個部分,一部分是對互聯網開放的服務器,另一部分是公司內部設備。其中對互聯網開放的部分分配公有地址,可以和互聯網直接進行通信。相對地,內網部分則分配私有地址,內網中的設備不能和互聯網直接收發網絡包,而是通過一種特別的機制進行連接,這個機制就叫地址轉換。

技術分享圖片

d.地址轉換(NAT)的基本原理

  地址轉換的基本原理是在轉發網絡包時對IP頭部中的IP地址和端口號進行改寫,如下圖所示:TCP連接操作的第一個包被轉發到互聯網時,會將發送方IP地址從私有地址改寫成公有地址。這裏使用的公有地址是地址轉換設備的互聯網接入端口的地址。與此同時,端口號也需要進行改寫,地址轉換設備會隨機選擇一個空閑的端口。然後,改寫前的私有地址和端口號,以及改寫後的公有地址和端口號,會作為一組相對應的記錄保存在地址轉換設備內部的一張表(NAT表)中。

技術分享圖片

  改寫發送方IP地址和端口號之後,包就被發往互聯網,最終到達服務器,然後服務器會返回一個包。服務器返回的包的接收包是原始包的發送方,因此返回的包的接收方就是改寫後的公有地址和端口號。這個公有地址其實是地址轉換設備的地址,因此這個返回包就會到達地址轉換設備。接下來,地址轉換設備會從地址對應表中通過公有地址和端口號找到相對應的私有地址和端口號,並改寫接收方信息,然後將包發給局域網的內部設備,這樣包就能夠到達原始的發送方了。

e.為什麽需要改寫端口號?

  早期的地址轉換機制是只改寫地址,不改寫端口號的。使用這種方法的前提是私有地址和公有地址必須一一對應,也就是說,有多少臺設備需要同時訪問互聯網,就需要多少個公有地址。訪問動作結束後可以刪除對應表中的記錄,這時同一個公有地址可以分配給其他設備使用。
  後續隨著互聯網的發展,同一個局域網裏的設備也越來越多。改寫端口號正是為了解決這個問題。客戶端一方的端口號本來就是從空閑端口中隨機選擇的,因此改寫了也不會有問題。端口號是一個16比特的數值,總共可以分配出幾萬個端口,因此如果用公有地址加上端口的組合對應一個私有地址,一個公有地址就可以對應幾萬個私有地址,這種方法提高了公有地址的利用率。

(3) NAT技術帶來的弊端

  首先,NAT使IP會話的保持時效變短。因為NAT表中的每一條記錄,在會話靜默的這段時間,NAT網關會進行老化操作。這是任何一個NAT網關必須做的事情,因為IP和端口資源有限,通信的需求無限,所以必須在會話結束後回收資源。通常TCP會話通過協商的方式主動關閉連接,NAT網關可以跟蹤這些報文,但總是存在例外的情況,要依賴自己的定時器(NAT超時機制)去回收資源。通過NAT超時機制回收會帶來一個問題,如果應用需要維持連接的時間大於NAT網關的設置,通信就會意外中斷。因為網關回收相關轉換表資源以後,新的數據到達時就找不到相關的轉換信息,必須建立新的連接。

  其次,NAT在實現上將多個內部主機發出的連接復用到一個IP上,這就使依賴IP進行主機跟蹤的機制都失效了。基於用戶行為的日誌分析也變得困難,因為一個IP被很多用戶共享,如果存在惡意的用戶行為,很難定位到發起連接的那個主機。NAT隱蔽了通信的一端,把簡單的事情復雜化了。

  NAT一下對IP端到端模型產生了破壞。NAT通過修改IP首部的信息變換通信的地址。但是在這個轉換過程中只能基於一個會話單位。當一個應用需要保持多個雙向連接時,麻煩就很大。NAT不能理解多個會話之間的關聯性,無法保證轉換符合應用需要的規則。當NAT網關擁有多個公有IP地址時,一組關聯會話可能被分配到不同的公網地址,這通常是服務器端無法接受的。更為嚴重的是,當公網側的主機要主動向私網側發送數據時,NAT網關沒有轉換這個連接需要的關聯表,這個數據包無法到達私網側的主機。這些反方向發送數據的連接總有應用協議的約定或在初始建立的會話中進行過協商。

(4) NAT超時機制對心跳保活

  上一節已經分析到,NAT超時機制會帶來一個問題,如果應用需要維持連接的時間大於NAT網關的設置,通信就會意外中斷。因為網關回收相關轉換表資源以後,新的數據到達時就找不到相關的轉換信息,必須建立新的連接。當這個新數據是由公網側向私網側發送時,就會發生無法觸發新連接建立,也不能通知到私網側的主機去重建連接的情況。這時候通信就會中斷,不能自動恢復。即使新數據是從私網側發向公網側,因為重建的會話表往往使用不同於之前的公網IP和端口地址,公網側主機也無法對應到之前的通信上,導致用戶可感知的連接中斷。
  所以普遍的一個做法就是使用心跳保活,在一段時間沒有數據需要發送時,主動發送一個NAT能感知到而又沒有實際數據的保活消息–心跳,這麽做的主要目的就是重置NAT的會話定時器。理想的情況下,客戶端應當以略小於NAT超時時間的間隔來發送心跳包。根據微信團隊測試的一些數據,一些常用網絡的NAT超時時間如下表所示:

地區/網絡NAT超時時間
中國移動3G和2G 5分鐘
中國聯通2G 5分鐘
中國電信3G 大於28分鐘
美國3G 大於28分鐘
臺灣3G 大於28分鐘

5.小結

  本文簡單的總結了一些短連接的劣勢,以及幾種改進的連接方案,並引出長連接的概念和相關的使用場景,並詳細對比了keep-alive和心跳機制的不同之處,強調心跳機制對長連接保活的重要意義。並對影響心跳時間的兩個關鍵–DHCP與NAT進行了簡單的介紹。現在動態心跳的方案也越來越普及,網上已經有不少文章做了相關的分享,本文參考文獻部分也有相關的鏈接。如有不當之處,敬請批評指正。


參考文獻

1. TCP Keepalive HOWTO
2. 移動端IM開發需要面對的技術問題
3. 為什麽說基於TCP的移動端IM仍然需要心跳保活
4. DHCP General Operation and Client Finite State Machine
5. dhcp.figure
6. Android 2.1 - 4.1.1 Allows DHCP Lease to Expire, Keeps Using IP Address。
7. 《網絡是怎麽連接的》–3.4.2節:地址轉換的基本原理
8. 《HTTP權威指南》–第4章:連接管理
9. 前端性能–淺談域名發散與域名收斂
10. Tcp Keepalive 和 HTTP Keepalive詳解
11.Android端消息推送總結:實現原理、心跳保活、遇到的問題等
12.P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介
13.知乎問答–NAT與DHCP的區別
14.一種Android端IM智能心跳算法的設計與實現探討

長連接及心跳保活原理簡介