HTTP事務(wù)的延遲—TCP的影響
最近看完了大部頭著作《HTTP權(quán)威指南》,對(duì)于此類(lèi)指南類(lèi)、手冊(cè)類(lèi)圖書(shū),往往讓我們聯(lián)想到的就是枯燥無(wú)味的使用講解、技術(shù)指標(biāo)講解......使人頭大。但是這本書(shū)卻讓我覺(jué)得讀起來(lái)很“清新”,一方面作者用了淺顯易懂的語(yǔ)言和大量的圖示讓我們很容易知所以然,另一方面應(yīng)該是我一直以來(lái)對(duì)網(wǎng)絡(luò)編程的興趣和此書(shū)的內(nèi)容有很大的契合點(diǎn),今天要講的內(nèi)容也是與自己的興趣有關(guān)的HTTP協(xié)議中有關(guān)TCP的部分,是從書(shū)中第四章——”連接管理“的部分內(nèi)容總結(jié)而來(lái)。
HTTP請(qǐng)求過(guò)程中會(huì)有哪些網(wǎng)絡(luò)時(shí)延?

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

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