HTTP事務的延遲—TCP的影響
最近看完了大部頭著作《HTTP權威指南》,對于此類指南類、手冊類圖書,往往讓我們聯想到的就是枯燥無味的使用講解、技術指標講解......使人頭大。但是這本書卻讓我覺得讀起來很“清新”,一方面作者用了淺顯易懂的語言和大量的圖示讓我們很容易知所以然,另一方面應該是我一直以來對網絡編程的興趣和此書的內容有很大的契合點,今天要講的內容也是與自己的興趣有關的HTTP協議中有關TCP的部分,是從書中第四章——”連接管理“的部分內容總結而來。
HTTP請求過程中會有哪些網絡時延?

域名解析:域名解析是進行網絡訪問的第一步,把域名識別為TCP認識的IP地址。這步往往會因為域名解析服務的質量造成諸多問題,我在實際的工程實踐中遇到的最常見的問題就是選擇的域名服務商質量不好或者客戶端本身設置的域名解析服務地址錯誤導致域名解析慢或者失敗。不過現在對于大多數的HTTP客戶端都有一個小的DNS緩存,用來保存近期所訪問站點的IP地址,可以有效的緩解此問題。
接下來,客戶端會向服務器發送一條TCP連接請求,并等待服務器回送一條響應。每條新的TCP連接都有連接建立時延(通常最多只有一兩秒鐘),對于單線程瀏覽器而言,如果有數百個并發的HTTP事務的話,可想而知時間疊加值也會很大。
一旦連接建立起來,客戶端和服務器端就會通過這個建立的TCP管道來進行請求的收發了,這些TCP的網絡時延的大小取決于硬件速度、網絡和服務器的負載,請求和響應報文的尺寸,以及客戶端和服務器之間的距離。請參見我的文章《構建高性能服務的考量》。
TCP相關時延
TCP連接建立握手

建立一條新的TCP連接時,TCP兩端之間會交換一系列的IP分組,如果連接只是用來傳送少量的數據,相比而這種建立連接的過程會大大影響HTTP的性能。
通常HTTP的事務都不會交換太多數據,SYN/SYN+ACK兩次握手會產生一個可測量的時延,但第三次握手——TCP連接ACK分組通常都足夠大,可以承載整個HTTP的請求報文(現代的TCP協議棧都允許客戶端在這個確認分組中發送數據),而且很多HTTP服務器響應報文都可以放到一個IP分組中去。
可以看出,小的HTTP事務可能會在TCP建立連接上花費50%的時間之多。所以我們現實中常常會有重用HTTP連接的需求。
TCP的延遲確認機制
我們都知道因特網自身是無法保證可靠的分組傳輸的,所以TCP實現了自己的確認機制來確保數據的傳輸成功。在這種確認機制中使用的就是確認報文,由于確認報文很小,于是TCP將要返回的確認信息與輸出的數據分組結合在一起發送可以更有效的利用網絡。為了增加確認報文找到同向傳輸數據分組的可能性,很多TCP協議棧都實現了一種“延遲確認”算法——在一個特定的窗口時間(通常是100-200毫秒)內將輸出確認放在緩沖區中,以尋找能夠捎帶它的輸出數據分組,否則超出這個時段就將確認數據單獨發送。
但是HTTP具有雙峰特征的請求——應答行為降低了捎帶信息的可能。當希望有相反方向回傳分組的時候,偏偏沒有那么多。通常,延遲確認算法會引入相當大的時延,所以我們應該依據相應的操作系統的不同調整或者禁用延遲確認算法。
注:在對TCP協議棧的任何參數進行修改之前,一定要對自己做什么有清醒的認識。TCP中引入這些算法的目的是防止設計欠佳的應用程序對網絡造成破壞。所以修改這些配置后都應該保證應用程序不會引發這些算法所要避免的問題。
TCP慢啟動(擁塞控制)
為了更好的保護網絡,TCP慢啟動限制了一個TCP端點在任意時刻可以傳輸的分組數。TCP數據傳輸起初會限制傳輸速度(傳輸分組數),如果數據成功傳輸,會隨著時間的推移提高傳輸速度(傳輸分組數)。如果某個HTTP事務有大量數據要發送,是不能一次將所有分組都發送出去的。必須依賴慢啟動逐漸的打開擁塞窗口。
由于存在這種擁塞控制特性,所以新連接的傳輸速度會比已經交換過一定量數據的連接慢一些。這樣又需要我們從重用HTTP連接(持久連接)的角度去考慮提高傳輸性能。
Nagle算法與TCP_NODELAY
如果TCP連接總是發送大量包含少量數據的分組,網絡的性能就會嚴重下降。Nagle算法就是試圖在發送一個分組之前,將大量TCP數據綁定在一起發送(鼓勵發送全尺寸的段,比如以太網上的段大小是1500字節,否則就進行緩存,要么當所有其他分組都被確認之后,Nagle算法才允許發送非全尺寸的分組),以提高網絡效率。
Nagle算法會引發幾種HTTP的性能問題。比如小的HTTP報文可能無法填滿一個分組,所以要緩存等待起來,要么就等待確認分組的抵達(確認分組的時延大概在100-200毫秒)。
所以HTTP應用程序常常會在自己的協議棧中設置參數TCP_NODELAY,禁用Nagle算法來提高性能。
TIME_WAIT累積與端口耗盡
關于TIME_WAIT狀態的解釋請看我的這篇博文《網絡編程釋疑之:TCP的TIME_WAIT狀態在服務器開發中的影響?》,之前TIME_WAIT時間的設置為2分種之多是因為早期路由器的處理速度還比較慢,但是現在高速路由器的使用已經大大弱化了這個問題,所以對于web服務器來說可以通過操作系統設置來減小TIME_WAIT狀態的持續時間,否則如果服務器存在大量的TIME_WAIT狀態會大大影響服務器的性能。至于端口耗盡的情況則是針對少量客戶端主機對web服務器進行基準測試時可能出現的問題,與TCP連接四元組有關。