https://www.jianshu.com/p/9fe2c140fa52css
首先,咱們要明確咱們談的是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三次握手和四次揮手的環節。socket
1.爲何要有KeepAlive?tcp
在談KeepAlive以前,咱們先來了解下簡單TCP知識(知識很簡單,高手直接忽略)。首先要明確的是在TCP層是沒有「請求」一說的,常常聽到在TCP層發送一個請求,這種說法是錯誤的。
TCP是一種通訊的方式,「請求」一詞是事務上的概念,HTTP協議是一種事務協議,若是說發送一個HTTP請求,這種說法就沒有問題。也常常聽到面試官反饋有些面試運維的同窗,基本的TCP三次握手的概念不清楚,面試官問TCP是如何創建連接,面試者上來就說,假如我是客戶端我發送一個請求給服務端,服務端發送一個請求給我。。。
這種一聽就知道對TCP基本概念不清楚。下面是我經過wireshark抓取的一個TCP創建握手的過程。(命令行基本上用TCPdump,後面咱們還會用這張圖說明問題):
如今我看只要看前3行,這就是TCP三次握手的完整創建過程,第一個報文SYN從發起方發出,第二個報文SYN,ACK是從被鏈接方發出,第三個報文ACK確認對方的SYN,ACK已經收到,以下圖:
可是數據實際上並無傳輸,請求是有數據的,第四個報文才是數據傳輸開始的過程,細心的讀者應該可以發現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失效
基於此,咱們旺旺須要加上應用層的心跳。這個須要本身實現,這裏就不展開了。
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(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
表示關閉。