服务器出现大量的TIME_WAIT状态怎么办?

从TCP四次挥手状态变迁中我们可以知道,主动断开方收到被动断开方的FIN+ACK之后,会进入TIME_WAIT状态。所以首先需要搞清楚TIME_WAIT状态出现于主动断开的一方。这里不讲客户端与服务端,因为主动断开方在客户端或者服务端都有可能。特别是如果一个服务器是作为反向代理,那么反向代理服务器面对上游服务器就是客户端的角色。还有一个情况就是例如http/1.1长连接的时候,客户端在超时时间内没有新的数据发送,那么服务器会主动挂断这个连接,在服务器上就会出现TIME_WAIT状态。

TIME_WAIT状态主要是为了解决:

1、主动断开方需要最后回复被动断开方一个ACK,这个ACK可能会丢失,需要重传。被动断开方在没收到ACK又达到了RTO之后会重新发送FIN+ACK,如果此时主动断开方直接断开连接,那么被动断开方就收不到这个FIN+ACK的ACK了。
2、如果没有TIME_WAIT状态,那么主动断开方的端口会释放出来,就有可能会被立即提供给另一个连接使用,这个时候如果被动断开方之前发送的还在网络中的包到达的时候,如果sequence number恰好对上了,这个时候会对新的连接造成错乱。

TIME_WAIT状态需要经过2MSL会消失。MSL 是 Maximum Segment Lifetime,报文最大生存时间,我的Linux经测试一个MSL是30s,也就是需要经过60s,TIME_WAIT状态消失。

TIME_WAIT状态过多的问题:

1、首先主动断开方的这个端口无法使用,需要等待TIME_WAIT状态结束后才能使用此端口发起新的连接。所以在一个非常繁忙的服务器上,端口也是一个非常紧张的资源。
2、建立tcp连接的双方会维护TCB(Transmission Control Block)用于保存连接使用的源端口、目的端口、目的 ip、序号、 应答序号、对方窗口大小、己方窗口大小、tcp 状态、tcp 输入/输出队列、应用层输出队列、tcp 的重传有关变量等,TCB占用的内存也不能释放,所以大量TIME_WAIT状态会占据大量内存。

搞明白TIME_WAIT出现原因,可以看出,解决TIME_WAIT状态过多的首要方法就是避免服务器频繁主动断开连接。

nginx作为http七层反向代理的时候,与上游服务器建立连接的时候充当客户端的角色,那么如果配置不当,在nginx服务器上会出现大量的TIME_WAIT状态

最常见的情况,nginx与上游的连接是短连接,那么处理完一个请求就要重新建立连接,之前建立好的连接就会被nginx主动挂断,这个连接会进入TIME_WAIT状态。在当前nginx默认与上游建立连接还是http/1.0,在与上游建立连接的时候Connection头部传递的是close,这种情况情况首先都要避免。

解决这个问题的办法就是在proxy_pass 的时候指定connection头部和http协议版本:
1、proxy_http_version 1.1; #指定使用http/1.1协议发起请求
2、proxy_set_header Connection “”; #默认的Connection头部传递的是close,这里置空,就是keep-alive

这个时候就已经解决了与上游服务器建立短连接的问题。

其次,nginx默认与上游服务器之间的每个连接中传输超过100个请求之后,就会断开这个连接。配置指令是keepalive_requests,那么可以适当调高这个值,使得每个连接上多传输一些请求。

Syntax:keepalive_requests number;
Default:keepalive_requests 100;
Context:upstream

还有一个,nginx在upstream的context中提供了keepalive指令,用于指定单个worker进程在缓存中保存的空闲连接的最大个数,用于什么场景呢?个人测试了一下是nginx与上游服务器建立的连接在空闲的时候不会立即挂断,而是会保留一段时间。以便接下来的请求使用减少频繁建立连接的开销。

Syntax:keepalive connections;
Default:
Context:upstream

keepalive_timeout用于控制这个空闲连接保持的最长时间,默认是60s,也就是60s之后再没有新请求到达,就会关闭这个连接

Syntax:keepalive_timeout timeout;
Default:keepalive_timeout 60s;
Context:upstream

除此之外,Linux内核还提供了一些参数用于对处于TIME_WAIT端口的复用,例如net.ipv4.tcp_tw_reuse=1表示Linux作为客户端的时候可以复用处于TIME_WAIT状态的端口,这个参数要打开TCP timestamp选项net.ipv4.tcp_timestamps = 1,也就是客户端复用了处于TIME_WAIT状态的端口之后,如果之前连接上有后续的报文到来,因为timestamp小于新建连接的SYN状态的timestamp,就可以知道这个报文不是新建连接的,会向对端发送RST报文予以回复。

同时,我们可以增大TIME_WAIT状态的数量,通过net.ipv4.tcp_max_tw_buckets来指定,超过这个数量将直接关闭连接。但是要注意,我们首先要搞清楚服务器上是否存在大量短连接,为什么服务器上会频繁主动断开才是分析和解决TIME_WAIT状态的关键,其次才是根据服务器上的硬件配置适当调大这个值。

留下评论