根據TCP協議,主動發起關閉的一方,會進入TIME_WAIT狀態,持續2*MSL(Max
Segment Lifetime),預設為240秒,在這個 post 中簡潔的介紹了為什麼需要這個狀態。
值得一說的是,對於基於TCP的HTTP協議,關閉TCP連接的是Server端,這樣,Server端會進入TIME_WAIT狀態,可想而知,對於訪問量大的Web
Server,會存在大量的TIME_WAIT狀態,假如server一秒鐘接收1000個請求,那麼就會積壓240*1000=240,000個TIME_WAIT的記錄,維護這些狀態給Server帶來負擔。當然現代操作系統都會用快速的查找算法來管理這些TIME_WAIT,所以對於新的TCP連接請求,判斷是否hit中一個TIME_WAIT不會太費時間,但是有這麼多狀態要維護總是不好。
HTTP協議1.1版規定default行為是Keep-Alive,
也就是會重用TCP連接傳輸多個request/response,一個主要原因就是發現了這個問題。
還有一個方法減緩TIME_WAIT壓力就是把系統的2*MSL時間減少,
因為240秒的時間實在是太長了點,對於Windows,修改注冊表,
在HKEY_LOCAL_MACHINE
\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters上添加一個DWORD類型的值TcpTimedWaitDelay,一般認為不要少於60,不然可能會有麻煩。
對於大型的服務,一台server搞不定,需要一個LB(Load
Balancer)把流量分配到若干後端服務器上,如果這個LB是以NAT方式工作的話,可能會帶來問題。
假如所有從LB到後端Server的IP包的source
address都是一樣的(LB的對內地址),那麼LB到後端Server的TCP連接會受限制,
因為頻繁的TCP連接建立和關閉,會在Server上留下 TIME_WAIT狀態,而且這些狀態對應的remote
address都是LB的,LB的source
port撐死也就60000多個 (2^16=65536,1~1023是保留端口,還有一些其他端口預設也不會用),每個LB上的端口一旦進入Server的TIME_WAIT黑名單,就有240秒不能再用來建立和Server的連接,
這樣LB和Server最多也就能支持300個左右的連接。
如果沒有LB,不會有這個問題,因為這樣server看到的remote
address是internet上廣闊無垠的集合,對每個address,60000多個port實在是夠用了。
一開始我覺得用上LB會很大程度上限制TCP的連接數,
但是實驗表明沒這回事,LB後面的一台Windows
Server 2003每秒處理請求數照樣達到了600個,難道TIME_WAIT狀態沒起作用?
用Net
Monitor和netstat觀察后發現,Server和LB的XXXX端口之間的連接進入TIME_WAIT狀態后,再來一個LB的XXXX端口的SYN包,Server照樣接收處理了,而不是想像的那樣被drop掉了。
翻書,從書堆里面找出覆滿塵土的大學時代買的《UNIX
Network Programming, Volume 1, Second Edition: Networking APIs:
Sockets and XTI》,中間提到一句,對於BSD-derived實現,只要SYN的sequence
number比上一次關閉時的最大sequence
number還要大,那麼
TIME_WAIT
狀態一樣接受這個SYN,難不成Windows也算BSD-derived?
有了這點線索和關鍵字(BSD),找到這個post,在NT4.0的時候,還是和BSD-derived不一樣的,不過Windows
Server 2003已經是NT5.2了,也許有點差別了。
做個試驗,用Socket
API編一個Client端,每次都Bind到本地一個端口比如2345,重複的建立TCP連接往一個Server發送Keep-Alive=false的HTTP請求,Windows的實現讓sequence
number不斷的增長,所以雖然Server對於Client 的2345端口連接保持TIME_WAIT狀態,但是總是能夠接受新的請求,不會拒絕。
那如果SYN的Sequence
Number變小會怎麼樣呢?同樣用Socket
API,不過這次用Raw
IP,發送一個小sequence
number的SYN包過去,Net
Monitor里面看到,這個SYN被Server接收後如泥牛如海,一點反應沒有,被drop掉了。
按照書上的說法,BSD-derived和Windows
Server
2003的做法有安全隱憂,不過至少這樣,至少不會出現TIME_WAIT阻止TCP請求的問題,當然,客戶端要配合,保證不同TCP連接的sequence
number要上漲不要下降。