什麼是HTTP Keep-Alive呢?

在經過調試工具查看網絡請求的時候,一般在response header能看到相似下面這種:Keep-Alive: timeout=10, max=94 。那麼Keep-Alive究竟是什麼呢?php

HTTP Keep-Alive

在http早期,每一個http請求都要求打開一個tpc socket鏈接,而且使用一次以後就斷開這個tcp鏈接。nginx

使用keep-alive能夠改善這種狀態,即在一次TCP鏈接中能夠持續發送多份數據而不會斷開鏈接。經過使用keep-alive機制,能夠減小tcp鏈接創建次數,也意味着能夠減小TIME_WAIT狀態鏈接,以此提升性能和提升httpd服務器的吞吐率(更少的tcp鏈接意味着更少的系統內核調用,socket的accept()和close()調用)。瀏覽器

可是,keep-alive並非免費的午飯,長時間的tcp鏈接容易致使系統資源無效佔用。配置不當的keep-alive,有時比重複利用鏈接帶來的損失還更大。因此,正確地設置keep-alive timeout時間很是重要。服務器

keepalvie timeout

Httpd守護進程,通常都提供了keep-alive timeout時間設置參數。好比nginx的keepalive_timeout,和Apache的KeepAliveTimeout。這個keepalive_timout時間值意味着:一個http產生的tcp鏈接在傳送完最後一個響應後,還須要hold住keepalive_timeout秒後,纔開始關閉這個鏈接。網絡

當httpd守護進程發送完一個響應後,理應立刻主動關閉相應的tcp鏈接,設置 keepalive_timeout後,httpd守護進程會想說:」再等等吧,看看瀏覽器還有沒有請求過來」,這一等,即是keepalive_timeout時間。若是守護進程在這個等待的時間裏,一直沒有收到瀏覽發過來http請求,則關閉這個http鏈接。socket

下面寫一個腳本,方便測試:tcp

1    sleep(60);  //爲了便於分析測試,會根據測試進行調整
2    echo "www.example.com";

1. 當keepalive_timeout時間爲0時,即不啓用Keep-Alive時,一個tcp鏈接的生命週期:工具

01    #tcpdump -n host 218.1.57.236 and port 80
02    20:36:50.792731 IP 218.1.57.236.43052 > 222.73.211.215.http: S 1520902589:1520902589(0) win 65535
03    20:36:50.792798 IP 222.73.211.215.http > 218.1.57.236.43052: S 290378256:290378256(0) ack 1520902590 win 5840
04    20:36:50.801629 IP 218.1.57.236.43052 > 222.73.211.215.http: . ack 1 win 32768
05     
06    20:36:50.801838 IP 218.1.57.236.43052 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
07    20:36:50.801843 IP 222.73.211.215.http > 218.1.57.236.43052: . ack 797 win 59
08     
09    20:37:50.803230 IP 222.73.211.215.http > 218.1.57.236.43052: P 1:287(286) ack 797 win 59
10    20:37:50.803289 IP 222.73.211.215.http > 218.1.57.236.43052: F 287:287(0) ack 797 win 59
11    20:37:50.893396 IP 218.1.57.236.43052 > 222.73.211.215.http: . ack 288 win 32625
12    20:37:50.894249 IP 218.1.57.236.43052 > 222.73.211.215.http: F 797:797(0) ack 288 win 32625
13    20:37:50.894252 IP 222.73.211.215.http > 218.1.57.236.43052: . ack 798 win 59
  • 第1~3行創建tcp三次握手,創建鏈接。用時8898μs
  • 第4~5行經過創建的鏈接發送第一個http請求,服務端確認收到請求。用時5μs
  • 第5~6行,能夠知道腳本執行用時60s1387μs,與php腳本相符。
  • 第六、8行服務端發送http響應。發送響應用時90166μs。
  • 第7行,代表由服務端守護進程主動關閉鏈接。結合第六、8行,說明http響應一旦發送完畢,服務端立刻關閉這個tcp鏈接
  • 第七、九、10說明tcp鏈接順序關閉,用時90963μs。須要注意,這裏socket資源並無當即釋放,須要等待2MSL時間(60s)後才被真正釋放。

因而可知,在沒有設置 keepalive_timeout 狀況下,一個socket資源從創建到真正釋放須要通過的時間是:創建tcp鏈接 + 傳送http請求 + php腳本執行 + 傳送http響應 + 關閉tcp鏈接 + 2MSL 。(注:這裏的時間只能作參考,具體的時間主要由網絡帶寬,和響應大小而定)性能

2. 當keepalive_timeout時間大於0時,即啓用Keep-Alive時,一個tcp鏈接的生命週期。爲了便於分析,咱們將keepalive_timeout設置爲300s測試

01    #tcpdump -n host 218.1.57.236 and port 80
02    21:38:05.471129 IP 218.1.57.236.54049 > 222.73.211.215.http: S 1669618600:1669618600(0) win 65535
03    21:38:05.471140 IP 222.73.211.215.http > 218.1.57.236.54049: S 4166993862:4166993862(0) ack 1669618601 win 5840
04    21:38:05.481731 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 1 win 32768
05    21:38:05.481976 IP 218.1.57.236.54049 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
06    21:38:05.481985 IP 222.73.211.215.http > 218.1.57.236.54049: . ack 797 win 59
07     
08    21:38:07.483626 IP 222.73.211.215.http > 218.1.57.236.54049: P 1:326(325) ack 797 win 59
09    21:38:07.747614 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 326 win 32605
10    21:43:07.448454 IP 222.73.211.215.http > 218.1.57.236.54049: F 326:326(0) ack 797 win 59
11    21:43:07.560316 IP 218.1.57.236.54049 > 222.73.211.215.http: . ack 327 win 32605
12    21:43:11.759102 IP 218.1.57.236.54049 > 222.73.211.215.http: F 797:797(0) ack 327 win 32605
13    21:43:11.759111 IP 222.73.211.215.http > 218.1.57.236.54049: . ack 798 win 59

 

   
  • 咱們先看一下,第6~8行,跟上次示例不同的是,服務端httpd守護進程發完響應後,沒有當即主動關閉tcp鏈接。
  • 第8行,結合第6行,咱們能夠看到,5分鐘(300s)後,服務端主動關閉這個tcp鏈接。這個時間,正是咱們設置的keepalive_timeout的時間。
  • 因而可知,設置了keepalive_timout時間狀況下,一個socket創建到釋放須要的時間是多了keepalive_timeout時間。

3. 當keepalive_timeout時間大於0,而且在同一個tcp鏈接發送多個http響應。這裏爲了便於分析,咱們將keepalive_timeout設置爲180s

經過這個測試,咱們想弄清楚,keepalive_timeout是從第一個響應結束開啓計時,仍是最後一個響應結束開啓計時。測試結果證明是後者,這裏,咱們每隔120s發一次請求,經過一個tcp鏈接發送了3個請求。

01    # tcpdump -n host 218.1.57.236 and port 80
02    22:43:57.102448 IP 218.1.57.236.49955 > 222.73.211.215.http: S 4009392741:4009392741(0) win 65535
03    22:43:57.102527 IP 222.73.211.215.http > 218.1.57.236.49955: S 4036426778:4036426778(0) ack 4009392742 win 5840
04    22:43:57.111337 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1 win 32768
05     
06    22:43:57.111522 IP 218.1.57.236.49955 > 222.73.211.215.http: P 1:797(796) ack 1 win 32768
07    22:43:57.111530 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 797 win 59
08    22:43:59.114663 IP 222.73.211.215.http > 218.1.57.236.49955: P 1:326(325) ack 797 win 59
09    22:43:59.350143 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 326 win 32605
10     
11    22:45:59.226102 IP 218.1.57.236.49955 > 222.73.211.215.http: P 1593:2389(796) ack 650 win 32443
12    22:45:59.226109 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 2389 win 83
13    22:46:01.227187 IP 222.73.211.215.http > 218.1.57.236.49955: P 650:974(324) ack 2389 win 83
14    22:46:01.450364 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 974 win 32281
15     
16    22:47:57.377707 IP 218.1.57.236.49955 > 222.73.211.215.http: P 3185:3981(796) ack 1298 win 32119
17    22:47:57.377714 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 3981 win 108
18    22:47:59.379496 IP 222.73.211.215.http > 218.1.57.236.49955: P 1298:1622(324) ack 3981 win 108
19    22:47:59.628964 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1622 win 32768
20     
21    22:50:59.358537 IP 222.73.211.215.http > 218.1.57.236.49955: F 1622:1622(0) ack 3981 win 108
22    22:50:59.367911 IP 218.1.57.236.49955 > 222.73.211.215.http: . ack 1623 win 32768
23    22:50:59.686527 IP 218.1.57.236.49955 > 222.73.211.215.http: F 3981:3981(0) ack 1623 win 32768
24    22:50:59.686531 IP 222.73.211.215.http > 218.1.57.236.49955: . ack 3982 win 108

 

  • 第一組,三個ip包表示tcp三次握手創建鏈接,由瀏覽器創建。
  • 第二組,發送第一次http請求而且獲得響應,服務端守護進程輸出響應以後,並沒立刻主動關閉tcp鏈接。而是啓動keepalive_timout計時。
  • 第三組,2分鐘後,發送第二次http請求而且獲得響應,一樣服務端守護進程也沒有立刻主動關閉tcp鏈接,從新啓動keepalive_timout計時。
  • 第四組,又2分鐘後,發送了第三次http請求而且獲得響應。服務器守護進程依然沒有主動關地閉tcp鏈接(距第一次http響應有4分鐘了,大於keepalive_timeout值),而是從新啓動了keepalive_timout計時。
  • 第五組,跟最後一個響應keepalive_timeout(180s)內,守護進程再沒有收到請求。計時結束,服務端守護進程主動關閉鏈接。4次揮手後,服務端進入TIME_WAIT狀態。

這說明,當設定了keepalive_timeout,一個socket由創建到釋放,須要時間是:tcp創建 + (最後一個響應時間 – 第一個請求時間) + tcp關閉 + 2MSL。紅色加粗表示每一次請求發送時間、每一次請求腳本執行時間、每一次響應發送時間,還有兩兩請求相隔時間。進一步測試,正在關閉或者TIME_WAIT狀態的tcp鏈接,不能傳輸http請求和響應。即,當一個鏈接結束keepalive_timeout計時,服務端守護進程發送第一個FIN標誌ip包後,該鏈接不能再使用了。

http keep-alive與tcp keep-alive

http keep-alive與tcp keep-alive,不是同一回事,意圖不同。http keep-alive是爲了讓tcp活得更久一點,以便在同一個鏈接上傳送多個http,提升socket的效率。而tcp keep-alive是TCP的一種檢測TCP鏈接情況的保鮮機制。tcp keep-alive保鮮定時器,支持三個系統內核配置參數:

1    echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
2    echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
3    echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
 

keepalive是TCP保鮮定時器,當網絡兩端創建了TCP鏈接以後,閒置idle(雙方沒有任何數據流發送往來)了tcp_keepalive_time後,服務器內核就會嘗試向客戶端發送偵測包,來判斷TCP鏈接情況(有可能客戶端崩潰、強制關閉了應用、主機不可達等等)。若是沒有收到對方的回答(ack包),則會在 tcp_keepalive_intvl後再次嘗試發送偵測包,直到收到對對方的ack,若是一直沒有收到對方的ack,一共會嘗試 tcp_keepalive_probes次,每次的間隔時間在這裏分別是15s, 30s, 45s, 60s, 75s。若是嘗試tcp_keepalive_probes,依然沒有收到對方的ack包,則會丟棄該TCP鏈接。TCP鏈接默認閒置時間是2小時,通常設置爲30分鐘足夠了。

也就是說,僅當nginx的keepalive_timeout值設置高於tcp_keepalive_time,而且距此tcp鏈接傳輸的最後一個http響應,通過了tcp_keepalive_time時間以後,操做系統纔會發送偵測包來決定是否要丟棄這個TCP鏈接。通常不會出現這種狀況,除非你須要這樣作。

keep-alive與TIME_WAIT

使用http keep-alvie,能夠減小服務端TIME_WAIT數量(由於由服務端httpd守護進程主動關閉鏈接)。道理很簡單,相較而言,啓用keep-alive,創建的tcp鏈接更少了,天然要被關閉的tcp鏈接也相應更少了。

最後

我想用一張示意圖片來講明使用啓用keepalive的不一樣。另外,http keepalive是客戶端瀏覽器與服務端httpd守護進程協做的結果,因此,咱們另外安排篇幅介紹不一樣瀏覽器的各類狀況對keepalive的利用。

 
Keep-Alive模式,客戶端如何判斷請求所獲得的響應數據已經接收完成(或者說如何知道服務器已經發生完了數據)?
1. 使用消息首部字段Conent-Length

故名思意,Conent-Length表示實體內容長度,客戶端(服務器)能夠根據這個值來判斷數據是否接收完成。可是若是消息中沒有Conent-Length,那該如何來判斷呢?又在什麼狀況下會沒有Conent-Length呢?請繼續往下看……

相關文章
相關標籤/搜索