问题现象:
在高并发的压力测试时候,有时候会TPS不稳定或者上不去的现象,同时观察到队列产生大量的TIME_WAIT。
基本的分析思路:
使用netstat -an|find "TCP",发现大量的TIME_WAIT状态的队列。以至于部分客户端连接不上去,导致TPS不稳定或者上不去,成功率下降。
发现这种情况表示有较多的队列在等待,原因是服务器端或者客户端的连接数有限制。
可能的情况有:服务器系统端口数量不够。
我们先来看一张图:
TCP连接的建立可以简单的称为三次握手,而连接的中止则可以叫做四次握手。
建立连接
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据,也就是ESTABLISHED状态。
结束连接
TCP有一个特别的概念叫做half-close,这个概念是说,TCP的连接是全双工(可以同时发送和接收)连接,因此在关闭连接的时候,必须关闭传和送两个方向上的连接。客户机给服务器一个FIN为1 的TCP报文,然后服务器返回给客户端一个确认ACK报文,并且发送一个FIN报文,当客户机回复ACK报文后(四次握手),连接就结束了。
LISTEN: 表示监听状态。服务端调用了listen函数,可以开始accept连接了
SYN_SENT:表示客户端已经发送了SYN报文。当客户端调用connect函数发起连接时,首先发SYN给服务端,然后自己进入SYN_SENT状态,并等待服务端发送ACK+SYN。
SYN_RCVD:表示服务端收到客户端发送SYN报文。服务端收到这个报文后,进入SYN_RCVD状态,然后发送ACK+SYN给客户端。
ESTABLISHED:表示连接已经建立成功了。服务端发送完ACK+SYN后进入该状态,客户端收到ACK后也进入该状态。
FIN_WAIT_1:表示主动关闭连接。无论哪方调用close函数发送FIN报文都会进入这个这个状态。
CLOSE_WAIT:表示被动关闭方等待关闭。当收到对方调用close函数发送的FIN报文时,回应对方ACK报文,此时进入CLOSE_WAIT状态。
FIN_WAIT_2:表示被动关闭方同意关闭连接。主动关闭连接方收到被动关闭方返回的ACK后,会进入该状态。
LAST_ACK:表示被动关闭方发送FIN报文后,等待对方的ACK报文状态,当收到ACK后进入CLOSED状态。
TIME_WAIT:表示收到对方的FIN报文并发送了ACK报文,就等2MSL后即可回到CLOSED状态了。如果FIN_WAIT_1状态下,收到对方同时带FIN标志和ACK标志的报文时,可以直接进入TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSED:结束
TCP要保证在所有可能的情况下使得所有的数据都能够被投递。当你关闭一个socket时,主动关闭一端的socket将进入TIME_WAIT状态,而被动关闭一方则转入CLOSED状态,这的确能够保证所有的数据都被传输。当一个socket关闭的时候,是通过两端互发信息的四次握手过程完成的,当一端调用close()时,就说明本端没有数据再要发送了。这好似看来在握手完成以后,socket就都应该处于关闭CLOSED状态了。
但这有两个问题:
首先,我们没有任何机制保证最后的一个ACK能够正常传输。
第二,网络上仍然有可能有残余的数据包(wandering duplicates),我们也必须能够正常处理。
我们再来看看TIME_WAIT:
TIME_WAIT是TCP连接断开时必定会出现的状态。是没有办法避免掉的。
TCP连接是全双工的,因此每个方向必须单独进行关闭。客户端与服务器端建立TCP/IP连接后关闭socket,服务端连接的端口状态未TIME_WAIT.主动关闭的一方在发送最后一个ACK后,就会进入TIME_WAIT状态。
总结主要的原因有两点:
1、防止上一次连接中的包,迷路后重新出现,影响新的连接
2、可靠的关闭TCP连接: 在主动关闭方发送最后一个ACK(FIN),有可能会丢失,这个时候被动方会重新发送FIN,如果这时主动方处于closed状态,就会响应RST而不是ACK。所以主动方要处于TIME_WAIT状态,而不是CLOSED。
修改的方法:
增加服务器端口数量或者增加服务器的数量
缩短超时时间
具体修改方法:
运行:regedit
系统注册表:\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters下,新建2个DWORD值:
MaxUserPort设置为:65534,缺省为5000;
TcpTimedWaitDelay设置为:30, 缺省为240。 ----缩短该时间 (单位:s)