1. 程式人生 > >關於HTTP中的keep-alive

關於HTTP中的keep-alive

一. 關於HTTP

    首先,HTTP是超文字傳輸協議,是一個基於請求與響應模式的、無狀態的、應用層的協議,常基於TCP的連線方式,其主要特點有如下:

  1. 支援客戶/伺服器模式;

  2. 簡單快速:客戶向伺服器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與伺服器聯絡的型別不同。由於HTTP協議簡單,使得HTTP伺服器的程式規模小,因而通訊速度很快;

  3. 靈活:HTTP允許傳輸任意型別的資料物件。正在傳輸的型別由Content-Type加以標記;

  4. 無連線:無連線的含義是限制每次連線只處理一個請求。伺服器處理完客戶的請求,並收到客戶的應答後,即斷開連線。採用這種方式可以節省傳輸時間;

  5. 無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的資訊,則它必須重傳,這樣可能導致每次連線傳送的資料量增大。另一方面,在伺服器不需要先前資訊時它的應答就較快。

二. keep-alive

    既然上面提到了HTTP是基於請求與響應的,且最主要的兩個特點就是無連線和無狀態,但需要說明的是,雖然是無連線的,但其底層也就是傳輸層大多卻是基於TCP面向連線的通訊方式,因此,這裡的無連線指的是:當server端和client端進行通訊的時候,client端向server端發起請求,server端接收請求之後返回給client端一個響應,之後就會斷開不再繼續保持連線了;這樣有一個好處就是對於只有一次訪問的連線來說不僅節省資源還很高效,但很明顯,如果client端還想繼續多次訪問server端就需要重新建立連線也就是會多次進行TCP的“三次握手,四次揮手”的過程,這樣一來並沒有節省資源而且還很低效,因此,使用keep-alive(又稱持久連線、連線重用)

可以改善這種狀態,即在一次TCP連線中可以持續傳送多份資料而不會斷開連線。通過使用keep-alive機制,避免了建立或者重新建立連線的次數,也意味著可以減少TIME_WAIT狀態連線,以此提高效能和提高httpd伺服器的吞吐率(更少的TCP連線意味著更少的系統核心呼叫,socket的accept()和close()呼叫)。

    HTTP 1.0中keep-alive預設是關閉的,需要在HTTP頭加入"Connection: Keep-Alive",才能啟用Keep-Alive;HTTP 1.1中預設啟用Keep-Alive,如果加入"Connection: close ",才關閉。目前大部分瀏覽器都是用HTTP 1.1協議,也就是說預設都會發起Keep-Alive的連線請求了,所以是否能完成一個完整的Keep- Alive連線就看伺服器設定情況。

    其中,RFC 2616 (P47)指出:單使用者客戶端與任何伺服器或代理之間的連線數不應該超過2個。一個代理與其它伺服器或程式碼之間應該使用不超過2 * N的活躍併發連線。這是為了提高HTTP響應時間,避免擁塞(冗餘的連線並不能程式碼執行效能的提升)。

三. keep-alive timeout時間

    keep-alive並不是免費的午餐,長時間的TCP連線容易導致系統資源無效佔用,配置不當的keep-alive 有時比重複利用連線帶來的損失還更大;因此,正確地設定keep-alive timeout時間非常重要。

    httpd守護程序一般都提供了keep-alive timeout時間設定引數,比如nginx的keepalive_timeout和Apache的keepalivetimeout。這個keepalive_timeout時間值意味著:一個http產生的TCP連線在傳送完最後一個響應後,還需要保持keepalive_timeout時間後才開始關閉這個連線;

    在沒有設定keepalive_timeout的情況下,一個socket資源從建立到真正釋放所需要經過的時間是:建立TCP連線(三次握手)+傳送http請求+指令碼指向+傳送http響應+關閉TCP連線(四次揮手)+主動關閉的一方進入TIME_WAIT的2MSL等待時間

    當設定了keepalive_timeout時間之後,一個socket由建立到釋放所需要經過的時間是:TCP建立連線(三次握手)+(最後一次響應 - 第一次請求時間)+TCP關閉連線(四次揮手)+2MSL;也就是說,當使用keep-alive機制的時候,當一次請求-響應結束之後,這個連線還會繼續維持上keepalive_timeout時間,如果在這個時間內client端還有請求發過來,那麼server端會繼續處理給予響應,如果keepalive_timeout時間計時結束後,就會進入TCP釋放連線的階段,因此也就會結束掉這次通訊;

四. keep-alive模式的使用

    雖然keep-alive模式可以降低TCP連線的次數提高效率,但並不是什麼情況下都適合使用keep-alive機制的,如下舉個栗子:

比如很多網頁中圖片、CSS、JS、Html都在一臺Server上,當用戶訪問其中的Html網頁時,網頁中的圖片、Css、Js都構成了訪問請求,開啟KeepAlive屬性可以有效地降低TCP握手的次數(當然瀏覽器對同一域下同時請求的圖片數有限制,一般是2),減少httpd程序數,從而降低記憶體的使用(假定prefork模式)。MaxKeepAliveRequestsKeepAliveTimeOut兩個屬性在KeepAlive=On時起作用,可以控制持久連線的生存時間和最大服務請求數。 

             不過,上面說的只是一種情形,那就是靜態網頁居多的情況下,並且網頁中的其他請求與網頁在同一臺Server上。當你的應用動態程式(比如:php)居多,使用者訪問時由動態程式即時生成html內容,html內容中圖片素材和Css、Js等比較少或者雜湊在其他Server上時,KeepAlive=On反而會降低Apache的效能。為什麼呢?

             前面提到過,KeepAlive=On時,每次使用者訪問,開啟一個TCP連線,Apache都會保持該連線一段時間,以便該連線能連續為同一client服務,在KeepAliveTimeOut還沒到期並且MaxKeepAliveRequests還沒到閾值之前,Apache必然要有一個httpd程序來維持該連線,httpd程序不是廉價的,他要消耗記憶體和CPU時間片的。假如當前Apache每秒響應100個使用者訪問,KeepAliveTimeOut=5,此時httpd程序數就是100*5=500個(模式),一個httpd程序消耗5M記憶體的話,就是500*5M=2500M=2.5G,誇張吧?當然,Apache與Client只進行了100次TCP連線。如果你的記憶體夠大,系統負載不會太高,如果你的記憶體小於2.5G,就會用到Swap,頻繁的Swap切換會加重CPU的Load。
             現在我們關掉KeepAliveApache仍然每秒響應100個使用者訪問,因為我們將圖片、js、css等分離出去了,每次訪問只有1個request,此時httpd的程序數是100*1=100個,使用記憶體100*5M=500M,此時Apache與Client也是進行了100次TCP連線。效能卻提升了太多。

因此,總結:

  1. 當你的Server記憶體充足時,KeepAlive=On還是Off對系統性能影響不大;

  2. 當你的Server上靜態網頁(Html、圖片、Css、Js)居多時,建議開啟KeepAlive;

  3. 當你的Server多為動態請求(因為連線資料庫,對檔案系統訪問較多),KeepAlive關掉,會節省一定的記憶體,節省的記憶體正好可以作為檔案系統的Cache(vmstat命令中cache一列),降低I/O壓力;

PS:當KeepAlive=On時,KeepAliveTimeOut的設定其實也是一個問題,設定的過短,會導致Apache頻繁建立連線,給Cpu造成壓力,設定的過長,系統中就會堆積無用的Http連線,消耗掉大量記憶體,具體設定多少,可以進行不斷的調節,因你的網站瀏覽和伺服器配置而異。