KeepAlive

什麼是KeepAlive?

首先,我們要明確我們談的是TCPKeepAlive 還是HTTPKeep-Alive。TCP的KeepAlive和HTTP的Keep-Alive是完全不同的概念,不能混爲一談。實際上HTTP的KeepAlive寫法是Keep-Alive,跟TCP的KeepAlive寫法上也有不同。

  • TCP的keepalive是側重在保持客戶端和服務端的連接,一方會不定期發送心跳包給另一方,當一方端掉的時候,沒有斷掉的定時發送幾次心跳包,如果間隔發送幾次,對方都返回的是RST,而不是ACK,那麼就釋放當前鏈接。設想一下,如果tcp層沒有keepalive的機制,一旦一方斷開連接卻沒有發送FIN給另外一方的話,那麼另外一方會一直以爲這個連接還是存活的,幾天,幾月。那麼這對服務器資源的影響是很大的。
  • HTTP的keep-alive一般我們都會帶上中間的橫槓,普通的http連接是客戶端連接上服務端,然後結束請求後,由客戶端或者服務端進行http連接的關閉。下次再發送請求的時候,客戶端再發起一個連接,傳送數據,關閉連接。這麼個流程反覆。但是一旦客戶端發送connection:keep-alive頭給服務端,且服務端也接受這個keep-alive的話,兩邊對上暗號,這個連接就可以複用了,一個http處理完之後,另外一個http數據直接從這個連接走了。減少新建和斷開TCP連接的消耗。

二者的作用簡單來說:

HTTP協議的Keep-Alive意圖在於短時間內連接複用,希望可以短時間內在同一個連接上進行多次請求/響應。

TCP的KeepAlive機制意圖在於保活、心跳,檢測連接錯誤。當一個TCP連接兩端長時間沒有數據傳輸時(通常默認配置是2小時),發送keepalive探針,探測鏈接是否存活。

總之,記住HTTP的Keep-Alive和TCP的KeepAlive不是一回事。

tcp的keepalive是在ESTABLISH狀態的時候,雙方如何檢測連接的可用行。而http的keep-alive說的是如何避免進行重複的TCP三次握手和四次揮手的環節。

TCP的KeepAlive

1.爲什麼要有KeepAlive?

在談KeepAlive之前,我們先來了解下簡單TCP知識(知識很簡單,高手直接忽略)。首先要明確的是在TCP層是沒有“請求”一說的,經常聽到在TCP層發送一個請求,這種說法是錯誤的。

TCP是一種通信的方式,“請求”一詞是事務上的概念,HTTP協議是一種事務協議,如果說發送一個HTTP請求,這種說法就沒有問題。也經常聽到面試官反饋有些面試運維的同學,基本的TCP三次握手的概念不清楚,面試官問TCP是如何建立鏈接,面試者上來就說,假如我是客戶端我發送一個請求給服務端,服務端發送一個請求給我。。。

這種一聽就知道對TCP基本概念不清楚。下面是我通過wireshark抓取的一個TCP建立握手的過程。(命令行基本上用TCPdump,後面我們還會用這張圖說明問題):

TCP抓包

現在我看只要看前3行,這就是TCP三次握手的完整建立過程,第一個報文SYN從發起方發出,第二個報文SYN,ACK是從被連接方發出,第三個報文ACK確認對方的SYN,ACK已經收到,如下圖:

TCP建立連接過程

但是數據實際上並沒有傳輸,請求是有數據的,第四個報文才是數據傳輸開始的過程,細心的讀者應該能夠發現wireshark把第四個報文解析成HTTP協議,HTTP協議的GET方法和URI也解析出來,所以說TCP層是沒有請求的概念,HTTP協議是事務性協議纔有請求的概念,TCP報文承載HTTP協議的請求(Request)和響應(Response)。

現在纔是開始說明爲什麼要有KeepAlive。鏈接建立之後,如果應用程序或者上層協議一直不發送數據,或者隔很長時間才發送一次數據,當鏈接很久沒有數據報文傳輸時如何去確定對方還在線,到底是掉線了還是確實沒有數據傳輸,鏈接還需不需要保持,這種情況在TCP協議設計中是需要考慮到的。

TCP協議通過一種巧妙的方式去解決這個問題,當超過一段時間之後,TCP自動發送一個數據爲空的報文給對方,如果對方迴應了這個報文,說明對方還在線,鏈接可以繼續保持,如果對方沒有報文返回,並且重試了多次之後則認爲鏈接丟失,沒有必要保持鏈接。

2.怎麼開啓KeepAlive?

KeepAlive並不是默認開啓的,在Linux系統上沒有一個全局的選項去開啓TCP的KeepAlive。需要開啓KeepAlive的應用必須在TCP的socket中單獨開啓。Linux Kernel有三個選項影響到KeepAlive的行爲:

  • tcp_keepalive_time 7200// 距離上次傳送數據多少時間未收到新報文判斷爲開始檢測,單位秒,默認7200s
  • tcp_keepalive_intvl 75// 檢測開始每多少時間發送心跳包,單位秒,默認75s
  • tcp_keepalive_probes 9// 發送幾次心跳包對方未響應則close連接,默認9次

TCP socket也有三個選項和內核對應,通過setsockopt系統調用針對單獨的socket進行設置:

  • TCPKEEPCNT: 覆蓋 tcpkeepaliveprobes
  • TCPKEEPIDLE: 覆蓋 tcpkeepalivetime
  • TCPKEEPINTVL: 覆蓋 tcpkeepalive_intvl

舉個例子,以我的系統默認設置爲例,kernel默認設置的tcpkeepalivetime是7200s, 如果我在應用程序中針對socket開啓了KeepAlive,然後設置的TCP_KEEPIDLE爲60,那麼TCP協議棧在發現TCP鏈接空閒了60s沒有數據傳輸的時候就會發送第一個探測報文。

3. 需要注意,KeepAlive的不足和侷限性

其實,tcp自帶的keepalive還是有些不足之處的。

keepalive只能檢測連接是否存活,不能檢測連接是否可用。例如,某一方發生了死鎖,無法在連接上進行任何讀寫操作,但是操作系統仍然可以響應網絡層keepalive包。

TCP keepalive 機制依賴於操作系統的實現,靈活性不夠,默認關閉,且默認的 keepalive 心跳時間是 兩個小時, 時間較長。

代理(如socks proxy)、或者負載均衡器,會讓tcp keep-alive失效

基於此,我們旺旺需要加上應用層的心跳。這個需要自己實現,這裏就不展開了。

HTTP的Keep-Alive

1. HTTP爲什麼需要Keep-Alive?

通常一個網頁可能會有很多組成部分,除了文本內容,還會有諸如:js、css、圖片等靜態資源,有時還會異步發起AJAX請求。只有所有的資源都加載完畢後,我們看到網頁完整的內容。然而,一個網頁中,可能引入了幾十個js、css文件,上百張圖片,如果每請求一個資源,就創建一個連接,然後關閉,代價實在太大了。

基於此背景,我們希望連接能夠在短時間內得到複用,在加載同一個網頁中的內容時,儘量的複用連接,這就是HTTP協議中keep-alive屬性的作用。

  • HTTP的Keep-Alive是HTTP1.1默認開啓的功能。通過headers設置"Connection: close "關閉
  • 在HTTP1.0中是默認關閉的。通過headers設置"Connection: Keep-Alive"開啓。

對於客戶端來說,不論是瀏覽器,還是手機App,或者我們直接在Java代碼中使用HttpUrlConnection,只是負責在請求頭中設置Keep-Alive。Keep-Alive屬性保持連接的時間長短是由服務端決定的,通常配置都是在幾十秒左右。

TCP連接建立之後,HTTP協議使用TCP傳輸HTTP協議的請求(Request)和響應(Response)數據,一次完整的HTTP事務如下圖:

HTTP請求

 

這張圖簡化了HTTP(Req)和HTTP(Resp),實際上的請求和響應需要多個TCP報文。
從圖中可以發現一個完整的HTTP事務,有鏈接的建立,請求的發送,響應接收,斷開鏈接這四個過程,早期通過HTTP協議傳輸的數據以文本爲主,一個請求可能就把所有要返回的數據取到,但是,現在要展現一張完整的頁面需要很多個請求才能完成,如圖片.JS.CSS等,如果每一個HTTP請求都需要新建並斷開一個TCP,這個開銷是完全沒有必要的。

開啓HTTP Keep-Alive之後,能複用已有的TCP鏈接,當前一個請求已經響應完畢,服務器端沒有立即關閉TCP鏈接,而是等待一段時間接收瀏覽器端可能發送過來的第二個請求,通常瀏覽器在第一個請求返回之後會立即發送第二個請求,如果某一時刻只能有一個鏈接,同一個TCP鏈接處理的請求越多,開啓KeepAlive能節省的TCP建立和關閉的消耗就越多。

當然通常會啓用多個鏈接去從服務器器上請求資源,但是開啓了Keep-Alive之後,仍然能加快資源的加載速度。HTTP/1.1之後默認開啓Keep-Alive, 在HTTP的頭域中增加Connection選項。當設置爲Connection:keep-alive表示開啓,設置爲Connection:close表示關閉。

 

鏈接:https://www.jianshu.com/p/9fe2c140fa52

發佈了95 篇原創文章 · 獲贊 39 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章