资讯

展开

TIME_WAIT或者CLOSE_WAIT的原因以及如何解决

作者:快盘下载 人气:

TCP的四次挥手

TIME_WAIT或者CLOSE_WAIT的原因以及如何解决

MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”.

2MSL在RFC 793协议中给出的建议是两分钟, 但是在Linux下一般时30秒, 也就是说2MSL就是60秒.

CLOSE_WAIT 产生的原因

CLOSE_WAIT是被动关闭连接是形成的,根据TCP状态机,服务器端收到客户端发送的FIN,TCP协议栈会自动发送ACK,连接进入CLOSE_WAIT状态。但如果服务器端不执行SOCKET的CLOSE()操作,状态就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多CLOSE_WAIT状态的连接.

所以如果被动关闭端关闭SOCKET不及时, 例如: I/O线程被意外阻塞,或者I/O线程执行的用户自定义Task比例过高,导致I/O操作处理不及时,链路不能被及时释放.

通常,CLOSE_WAIT 状态在服务器停留时间很短,如果你发现大量的 CLOSE_WAIT 状态,那么就意味着被动关闭的一方没有及时发出 包,一般有如下可能:

(1) 程序问题:如果代码层面忘记了 CLOSE 相应的 socket 连接,那么自然不会发出 FIN 包,从而导致 CLOSE_WAIT 累积;或者代码不严谨,出现死循环之类的问题,导致即便后面写了 CLOSE 也永远执行不到。

(2) 响应太慢或者超时设置过小:如果连接双方不和谐,一方不耐烦直接 timeout,另一方却还在忙于耗时逻辑,就会导致 close 被延后。响应太慢是首要问题,不过换个角度看,也可能是 timeout 设置过小。

TIME_WAIT 产生的原因

TIME_WAIT的作用

简单说timewait之所以等待2MSL的时长,是为了避免因为网络丢包或者网络延迟而造成的tcp传输不可靠,而这个TIME_WAIT状态则可以最大限度的提升网络传输的可靠性。

同时TCP一般会禁止处于TIME_WAIT的连接上重建一个新的TCP连接, 这样做主要是为了避免新旧数据包出现串包的情况, 所以总结来说, TIME_WAIT的作用如下:

为实现TCP全双工连接的可靠释放为使旧的数据包在网络因过期而消失

TIME_WAIT状态过多的危害

在socket的TIME_WAIT状态结束之前,该socket所占用的本地端口号将一直无法释放。请注意客户端的端口总是有限的(65535), 耗尽了就会导致网络连接失败.在高并发(每秒几万qps)并且采用短连接方式进行交互的系统中运行一段时间后,系统中就会存在大量的time_wait状态,如果time_wait状态把系统所有可用端口都占完了且尚未被系统回收时,就会出现无法向服务端创建新的socket连接的情况。此时系统几乎停转,任何链接都不能建立。大量的time_wait状态也会系统一定的fd,内存和cpu资源,当然这个量一般比较小,并不是主要危害

解决方法

查看状态为TIME_WAIT的TCP连接。

$ netstat -tan |grep TIME_WAIT

统计TCP各种状态的连接数。

$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(i in S) print i, S[i]}'
方式一: 调整系统内核参数
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

或者将MSL值缩减, linux中MSL的值默认为60s,我们可以通过缩减MSL值来使得主动关闭连接一端由TIME_WAIT状态到关闭状态的时间减少。

但是这样做会导致延迟报文无法清除以及主动关闭连接一端不能收到重传来的FIN请求,也会影响很多基于TCP的应用的连接复用和调优。

所以在实际生产环境中,需要谨慎操作。

查看默认的MSL值

$ cat /proc/sys/net/ipv4/tcp_fin_timeout

修改

$echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout

或者$ vim /etc/sysctl.con

fnet.ipv4.tcp_fin_timeout = 30

优化完内核参数后,可以执行sysctl -p命令,来激活上面的设置永久生效

方式二:调整短链接为长链接

短连接 连接->传输数据->关闭连接 HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。 也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。长连接 连接->传输数据->保持连接 -> 传输数据-> 。。。->关闭连接。 长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。

从区别上可以看出,长连接比短连接从根本上减少了关闭连接的次数,减少了TIME_WAIT状态的产生数>量,在高并发的系统中,这种方式的改动非常有效果,可以明显减少系统TIME_WAIT的数量。

我们可以在客户端将HTTP请求头里connection的值设置为:keep-alive。将短连接改成长连接。

长连接比短连接从根本上减少了server去主动关闭连接的次数,减少了TIME_WAIT状态连接的产生。

在利用nginx做反向代理时,如果要设置成长连接,则需要设置成:

1.从client到nginx的连接是长连接。
2.从nginx到server的连接是长连接。

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 本站 删除。

加载全部内容

相关教程
猜你喜欢
用户评论
快盘暂不提供评论功能!