加载中...

Linux 出现大量time_wait解决方法


在生产业务中,我们偶尔会碰到部分服务器相应比较慢,查看sockets会发现有大量的time_wait.今天我们就分析下这个请求的原因

1.问题现象

netstat -na | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
LISTEN 13
ESTABLISHED 9
FIN_WAIT1 1
TIME_WAIT 3207
#这里可以看到有3207个time_wait的链接

2.原理分析

首先我们需要知道socket状态为TIME_WAIT的原因是什么,这里我们需要回顾下TCP的四次挥手

img

明确下图中符号的含义

  • FIN: 连接终止位
  • seq: 发送的第一个字节的序号
  • ACK: 确认报文段
  • ack: 确认号,希望收到的下一个数据的第一个字节的序号

刚开始双方都处于ESTABLISHED 状态,假设是客户端先发起关闭请求(这里的客户端是相对的)。四次挥手的过程如下:

  1. 第一次挥手: 客户端发送一个FIN报文(请求连接终止信号:FIN=1),报文中会指定一个序列号seq = u.并停止在发送信息,主动关闭TCP连接.此时客户端处于FIN_WAIT-1状态,等待服务端的确认

    FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;

  2. 第二次挥手: 服务端收到FIN报文后,明确客户端想要断开连接.会发送ACK报文,并且把客户端的seq+1作为ACK报文的序列号值,此时服务端处于CLOSE_WAITz状态

    CLOSE-WAIT - 等待从本地用户发来的连接中断请求;

    此时的 TCP 处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待 2)状态,等待服务端发出的连接释放报文段。

    FIN-WAIT-2 - 从远程TCP等待连接中断请求;

  3. 第三次挥手: 如果服务端也要断开连接,没有数据需要传输了.会和客户端的第一次挥手一样,发送FIN报文,且随机指定一个序列号.然后服务端处于LAST_ACK状态,等待客户端的确认

    LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;

  4. 第四次挥手: 客户端收到FIN之后,一样发送一个ACK应答报文(ack = w +1),且吧服务端的序列号+1作为seq

    ,此时客户端处于TIME_WAIT状态

    TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认;

    这个时候由服务端到客户端的 TCP 连接并未释放掉,需要经过时间等待计时器设置的时间 2MSL(一个报文的来回时间) 后才会进入 CLOSED 状态(这样做的目的是确保服务端收到自己的 ACK 报文。如果服务端在规定时间内没有收到客户端发来的 ACK 报文的话,服务端会重新发送 FIN 报文给客户端,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文给服务端)。服务端收到 ACK 报文之后,就关闭连接了,处于 CLOSED 状态。

通过这个流程我们可以发现有大量的TIME_WAIT状态的socket主要是客户端需要等待足够的时间确保服务端接受到请求

3.参数调优

根据上面分析TIME_WAIT主要是客户端保证服务端能正常接受请求在等待的状态,默认这个是2ML也就是4分钟,但是生产中这个配置建议可以更第一点30秒、1分钟、2分钟都是可以的

/etc/sysctl.conf添加配置

#优化TIME_WAIT
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 = 30
#修改系統默认的TIMEOUT时间

sysctl -p生效

稍等两分钟再次查看

netstat -na | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
LISTEN 13
ESTABLISHED 9
TIME_WAIT 18

已经大幅度减少了 降到了十位数


文章作者: huhuhahei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 huhuhahei !
评论
  目录