解决TIME_WAIT、CLOSE_WAIT过多问题

2020/04/10

服务器大量处于TIME_WAIT状态,可能是什么原因,会造成什么影响,怎么解决?CLOSE_WAIT状态呢?

1. TIME_WAIT与CLOSE_WAIT产生原因

TIME_WAIT与CLOSE_WAIT 发生于客户端与服务器断开连接时四次挥手协议。

image-20201124093843977

表示 TCP 断开连接的时候,需要客户端和服务端总共发送 4 个包以确认连接的断开;客户端或服务器均可主动发起挥手动作(因为 TCP 是一个全双工协议),在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。

上面的一次socket关闭操作,我们可以得出以下几点:

  1. 主动关闭连接的一方,也就是主动调用socket的close操作的一方,最终会进入TIME_WAIT状态

  2. 被动关闭连接的一方,有一个中间状态,即CLOSE_WAIT,因为协议层在等待上层的应用程序,主动调close操作后才主动关闭这条连接

  3. TIME_WAIT会默认等待2MSL时间后,才最终进入CLOSED状态。

  4. 在一个连接没有进入CLOSED状态之前,这个连接是不能被重用的!

所以,TIME_WAIT并不可怕,CLOSE_WAIT才可怕,因为CLOSE_WAIT很多,表示说要么是你的应用程序写的有问题,没有合适的关闭socket;要么是说,你的服务器CPU处理不过来(CPU太忙)或者你的应用程序一直睡眠到其它地方(锁,或者文件I/O等等),你的应用程序获得不到合适的调度时间,造成你的程序没法真正的执行close操作。

2. TIME_WAIT 状态存在的理由

  • 可靠地实现TCP全双工连接的终止

在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。

因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。

  • 允许老的重复分节在网络中消逝

TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。

在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。

为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。

3. TIME_WAIT与CLOSE_WAIT解决办法

1.png

3.1 查询TCP连接数

netstat -ant|awk '/^tcp/ {++state[$NF]} END {for(key in state) print (key,state[key])}'

结果显示如下:

LISTEN 70
CLOSE_WAIT 1
ESTABLISHED 283
FIN_WAIT2 1
TIME_WAIT 170

状态描述:

CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉

3.2 TIME_WAIT解决方案

编辑内核文件/etc/sysctl.conf,打开系统的TIMEWAIT重用和快速回收,加入以下内容:

net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间

然后执行 /sbin/sysctl -p 让参数生效.

如果性能不太理想,加入如下配置尝试:

net.ipv4.tcp_keepalive_time = 1200 
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.ip_local_port_range = 1024 65000 
#表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192 
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_tw_buckets = 5000 
#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。
默认为180000,改为5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于 Squid,效果却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。

3.3 CLOSE_WAIT解决方案

检查代码!!!


微信扫描二维码

(转载本站文章请注明作者和出处 鲁先生-hanlinsir

Show Disqus Comments

Post Directory

扫码:鲁先生
发送 290992
即可立即永久解锁本站全部文章