問題現(xiàn)象:
在高并發(fā)的壓力測試時候,有時候會TPS不穩(wěn)定或者上不去的現(xiàn)象渊迁,同時觀察到隊列產生大量的TIME_WAIT慰照。
基本的分析思路:
使用netstat -an|find "TCP",發(fā)現(xiàn)大量的TIME_WAIT狀態(tài)的隊列琉朽。以至于部分客戶端連接不上去毒租,導致TPS不穩(wěn)定或者上不去,成功率下降箱叁。
發(fā)現(xiàn)這種情況表示有較多的隊列在等待墅垮,原因是服務器端或者客戶端的連接數(shù)有限制。
可能的情況有:服務器系統(tǒng)端口數(shù)量不夠蝌蹂。
我們先來看一張圖:
TCP連接的建立可以簡單的稱為三次握手噩斟,而連接的中止則可以叫做四次握手。
建立連接
在TCP/IP協(xié)議中孤个,TCP協(xié)議提供可靠的連接服務剃允,采用三次握手建立一個連接。
第一次握手:建立連接時,客戶端發(fā)送syn包(syn=j)到服務器斥废,并進入SYN_SEND狀態(tài)赴穗,等待服務器確認;
第二次握手:服務器收到syn包迎罗,必須確認客戶的SYN(ack=j+1)钻注,同時自己也發(fā)送一個SYN包(syn=k),即SYN+ACK包统锤,此時服務器進入SYN_RECV狀態(tài)毛俏;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發(fā)送確認包ACK(ack=k+1)饲窿,此包發(fā)送完畢煌寇,客戶端和服務器進入ESTABLISHED狀態(tài),完成三次握手逾雄。
完成三次握手阀溶,客戶端與服務器開始傳送數(shù)據(jù),也就是ESTABLISHED狀態(tài)鸦泳。
結束連接
TCP有一個特別的概念叫做half-close银锻,這個概念是說,TCP的連接是全雙工(可以同時發(fā)送和接收)連接做鹰,因此在關閉連接的時候击纬,必須關閉傳和送兩個方向上的連接〖佤铮客戶機給服務器一個FIN為1 的TCP報文掉弛,然后服務器返回給客戶端一個確認ACK報文,并且發(fā)送一個FIN報文喂走,當客戶機回復ACK報文后(四次握手)殃饿,連接就結束了。
LISTEN: 表示監(jiān)聽狀態(tài)芋肠。服務端調用了listen函數(shù)乎芳,可以開始accept連接了
SYN_SENT:表示客戶端已經(jīng)發(fā)送了SYN報文。當客戶端調用connect函數(shù)發(fā)起連接時帖池,首先發(fā)SYN給服務端奈惑,然后自己進入SYN_SENT狀態(tài),并等待服務端發(fā)送ACK+SYN睡汹。
SYN_RCVD:表示服務端收到客戶端發(fā)送SYN報文肴甸。服務端收到這個報文后,進入SYN_RCVD狀態(tài)囚巴,然后發(fā)送ACK+SYN給客戶端原在。
ESTABLISHED:表示連接已經(jīng)建立成功了友扰。服務端發(fā)送完ACK+SYN后進入該狀態(tài),客戶端收到ACK后也進入該狀態(tài)庶柿。
FIN_WAIT_1:表示主動關閉連接村怪。無論哪方調用close函數(shù)發(fā)送FIN報文都會進入這個這個狀態(tài)。
CLOSE_WAIT:表示被動關閉方等待關閉浮庐。當收到對方調用close函數(shù)發(fā)送的FIN報文時甚负,回應對方ACK報文,此時進入CLOSE_WAIT狀態(tài)审残。
FIN_WAIT_2:表示被動關閉方同意關閉連接梭域。主動關閉連接方收到被動關閉方返回的ACK后,會進入該狀態(tài)搅轿。
LAST_ACK:表示被動關閉方發(fā)送FIN報文后碰辅,等待對方的ACK報文狀態(tài),當收到ACK后進入CLOSED狀態(tài)介时。
TIME_WAIT:表示收到對方的FIN報文并發(fā)送了ACK報文,就等2MSL后即可回到CLOSED狀態(tài)了凌彬。如果FIN_WAIT_1狀態(tài)下沸柔,收到對方同時帶FIN標志和ACK標志的報文時,可以直接進入TIME_WAIT狀態(tài)铲敛,而無須經(jīng)過FIN_WAIT_2狀態(tài)褐澎。
CLOSED:結束
TCP要保證在所有可能的情況下使得所有的數(shù)據(jù)都能夠被投遞。當你關閉一個socket時伐蒋,主動關閉一端的socket將進入TIME_WAIT狀態(tài)工三,而被動關閉一方則轉入CLOSED狀態(tài),這的確能夠保證所有的數(shù)據(jù)都被傳輸先鱼。當一個socket關閉的時候俭正,是通過兩端互發(fā)信息的四次握手過程完成的,當一端調用close()時焙畔,就說明本端沒有數(shù)據(jù)再要發(fā)送了掸读。這好似看來在握手完成以后,socket就都應該處于關閉CLOSED狀態(tài)了宏多。
但這有兩個問題:
首先儿惫,我們沒有任何機制保證最后的一個ACK能夠正常傳輸。
第二伸但,網(wǎng)絡上仍然有可能有殘余的數(shù)據(jù)包(wandering duplicates)肾请,我們也必須能夠正常處理。
我們再來看看TIME_WAIT:
TIME_WAIT是TCP連接斷開時必定會出現(xiàn)的狀態(tài)更胖。是沒有辦法避免掉的铛铁。
TCP連接是全雙工的隔显,因此每個方向必須單獨進行關閉”芄椋客戶端與服務器端建立TCP/IP連接后關閉socket荣月,服務端連接的端口狀態(tài)未TIME_WAIT.主動關閉的一方在發(fā)送最后一個ACK后,就會進入TIME_WAIT狀態(tài)梳毙。
總結主要的原因有兩點:
1哺窄、防止上一次連接中的包,迷路后重新出現(xiàn)账锹,影響新的連接
2萌业、可靠的關閉TCP連接: 在主動關閉方發(fā)送最后一個ACK(FIN),有可能會丟失,這個時候被動方會重新發(fā)送FIN奸柬,如果這時主動方處于closed狀態(tài)生年,就會響應RST而不是ACK。所以主動方要處于TIME_WAIT狀態(tài)廓奕,而不是CLOSED抱婉。
修改的方法:
增加服務器端口數(shù)量或者增加服務器的數(shù)量
縮短超時時間
具體修改方法:
運行:regedit
系統(tǒng)注冊表:\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters下,新建2個DWORD值:
MaxUserPort設置為:65534桌粉,缺省為5000蒸绩;
TcpTimedWaitDelay設置為:30, 缺省為240铃肯。 ?----縮短該時間 ?(單位:s)