什麼是KeepAlive?
首先,我們要明確我們談的是TCP的 KeepAlive 還是HTTP的 Keep-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