1. 程式人生 > >淺談Http長連線和Keep-Alive以及Tcp的Keepalive

淺談Http長連線和Keep-Alive以及Tcp的Keepalive

Keep-Alive模式:
我們知道Http協議採用“請求-應答”模式,當使用普通模式,即非Keep-Alive模式時,每個請求/應答,客戶端和伺服器都要新建一個連線,完成之後立即斷開連線;當使用Keep-Alive模式時,Keep-Alive功能使客戶端到伺服器端的連線持續有效,當出現對伺服器的後繼請求時,Keep-Alive功能避免了建立或者重新建立連線。
http1.0中預設是關閉的,需要在http頭加入”Connection: Keep-Alive”,才能啟用Keep-Alive;
http 1.1中預設啟用Keep-Alive,如果加入”Connection: close “才關閉。目前大部分瀏覽器都是用http1.1協議,也就是說預設都會發起Keep-Alive的連線請求了,所以是否能完成一個完整的Keep- Alive連線就看伺服器設定情況。下圖是普通模式和長連線模式的請求對比:
這裡寫圖片描述

開啟Keep-Alive的優缺點:
優點:Keep-Alive模式更加高效,因為避免了連線建立和釋放的開銷。
缺點:長時間的Tcp連線容易導致系統資源無效佔用,浪費系統資源。

當保持長連線時,如何判斷一次請求已經完成?
Content-Length
Content-Length表示實體內容的長度。瀏覽器通過這個欄位來判斷當前請求的資料是否已經全部接收。
所以,當瀏覽器請求的是一個靜態資源時,即伺服器能明確知道返回內容的長度時,可以設定Content-Length來控制請求的結束。但當伺服器並不知道請求結果的長度時,如一個動態的頁面或者資料,Content-Length就無法解決上面的問題,這個時候就需要用到Transfer-Encoding欄位。

Transfer-Encoding
Transfer-Encoding是指傳輸編碼,在上面的問題中,當服務端無法知道實體內容的長度時,就可以通過指定Transfer-Encoding: chunked來告知瀏覽器當前的編碼是將資料分成一塊一塊傳遞的。當然, 還可以指定Transfer-Encoding: gzip, chunked表明實體內容不僅是gzip壓縮的,還是分塊傳遞的。最後,當瀏覽器接收到一個長度為0的chunked時, 知道當前請求內容已全部接收。

Keep-Alive 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連線。

Tcp的Keepalive:
連線建立之後,如果客戶端一直不傳送資料,或者隔很長時間才傳送一次資料,當連線很久沒有資料報文傳輸時如何去確定對方還線上,到底是掉線了還是確實沒有資料傳輸,連線還需不需要保持,這種情況在TCP協議設計中是需要考慮到的。
TCP協議通過一種巧妙的方式去解決這個問題,當超過一段時間之後,TCP自動傳送一個數據為空的報文(偵測包)給對方,如果對方迴應了這個報文,說明對方還線上,連線可以繼續保持,如果對方沒有報文返回,並且重試了多次之後則認為連結丟失,沒有必要保持連線。

tcp keep-alive是TCP的一種檢測TCP連線狀況的保鮮機制。tcp keep-alive保鮮定時器,支援三個系統核心配置引數:
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_time = 1800
keepalive是TCP保鮮定時器,當網路兩端建立了TCP連線之後,閒置(雙方沒有任何資料流傳送往來)了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分鐘足夠了。