出现大量TIME_WAIT时的解决办法

日前内网的某个服务出现了总是返回失败的情况,输入 netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 排查时发现该上机器上有大量的 TIME_WAIT 连接。并且服务日志中有 Too manay open files的报错。

排查服务端程序问题

遇到这个问题时,首先想到的是程序本身是否有问题。服务端是用 golang 写的,,使用 net/http 包的 Client 时导致出现 TIME_WAIT 连接数过多主要有以下两种情况:

  • 情形1:由于忘记读取响应的body导致创建大量处于TIME_WAIT状态的连接
  • 情形2:连接的数量超过连接池的限制导致出现大量TIME_WAIT状态的连接
    这种情况时由于持续超过连接池导致许多短链接被打开。

排查服务端程序后,未发现程序出现此类问题,因此从其他方向排查。

优化内核参数

执行 ulimit -a 查看文件描述符限制,发现 open files 一行的值为 1024。在当前的压力下,这个值显然过低了。
修改 /etc/security/limits.conf 来增大最大句柄数,

1
* - nofile 65535

同时,为了让服务器能够快速回收和重用那些TIME_WAIT的资源,让每个TIME_WAIT早点过期。修改 /etc/sysctl.conf 增加以下内容:

1
2
3
4
net.ipv4.tcp_syncookies = 1  
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

其中各参数含义如下
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时间

执行 sysctl –p 使内核参数生效

再用 netstat 查看,可以发现 TIME_WAIT 连接数快速下降,问题解决。

参考链接

使用golang的http.Client容易出现TIME_WAIT上涨的几种情况和解决方案
web服务器下出现大量TIME_WAIT
linux服务器出现大量TIME_WAIT的解决方法