最近遇到了tcp_tw_recycle参数引发网络不稳定的奇怪问题,其根本原因在于客户方系统切换到NAT环境,而NAT环境下,服务端开启tcp_tw_recycle会启用tcp time_wait的快速回收就会引发不稳定问题。

结合这个情况,并考虑到Linux 从4.12内核版本开始移除了 tcp_tw_recycle 配置,最后关闭tcp_tw_recycle 解决问题。

故障描述:

某个时刻客户方反馈调用我们接口有时候会出现访问异常,特别最近出现频率较高。

故障分析:

1、登录服务器检查资源,并使用top和vmstat等命令进行检查。发现服务器各项指标正常。于是问题转向了网络层。

2、客户端值班人员反映,他们一个系统访问我们接口才会一定频率异常,另一个系统访问我们接口则正常。

3、本地使用ping服务器外网ip返回正常,无丢包和正常延时。

4、使用http-ping工具时,会出现问题,并且经常出现连接失败:

附上http-ping工具下载地址:

https://www.softpedia.com/get/Network-Tools/IP-Tools/http-ping.shtml

访问异常截图

5、在访问异常的时候,使用tcpdump抓包的时候会抓到大量的syn请求,但服务器没有响应

6、登录服务器查看tcp相关数据

1
2
3
$ netstat -s | grep -i listen
    356 times the listen queue of a socket overflowed
    736790 SYNs to LISTEN sockets dropped

发现在异常时有大量tcp syn包被丢弃,数值一直在增长。

故障处理:

经过查阅资料,结合实际情况,发现服务器同时启用了tcp_timestamps和tcp_tw_recycle参数。

后来我想起来同事们为了改善time_wait连接太多的问题,修改了这个内核参数。

解决方法是关闭tcp_tw_recycle。

1
2
3
4
5
$ vi  /etc/sysctl.conf
#修改为如下
net.ipv4.tcp_tw_recycle = 0
#保存退出,使之生效
$ sysctl -p

再观察,发现服务已正常,异常现象消失。

分析总结:

我们先来看一下这两个参数:

tcp_timestamp

1
2
 tcp_timestamps (Boolean; default: enabled; since Linux 2.2)
              Enable RFC 1323 TCP timestamps.

tcp_timestamp 是 RFC1323 定义的优化选项,主要用于 TCP 连接中 RTT(Round Trip Time) 的计算,开启 tcp_timestamp 有利于系统计算更加准确的 RTT,也就有利于 TCP 性能的提升。(默认开启)

关于tcp_timestamps详情请见:

https://tools.ietf.org/pdf/rfc7323.pdf

tcp_tw_recycle

1
2
3
4
5
 tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)
              Enable fast recycling of TIME_WAIT sockets.  
              Enabling this option is not recommended 
              since this causes problems when working with NAT (Network Address
              Translation).

开启tcp_tw_recycle会启用tcp time_wait的快速回收,不建议在NAT环境中启用这个参数,它会引起相关问题。

tcp_tw_recycle是依赖tcp_timestamps参数的,在一般的网络环境下,可能没有问题,但是在NAT环境下,问题就来了。

比如我遇到的这种情况,办公室的外网地址只有一个。所有人访问后台都会通过路由器SNAT将内部网络地址映射到公网IP。因为服务器和客户端都启用了tcp_timestamps,所以时间戳信息被添加到tcp报头中。从服务器的角度来看,同一个客户端的时间戳必然是线性增加的。但是,由于我的客户端的网络环境是NAT,因此,每个主机的时间戳是不同的。tcp_tw_recycle启用后,一旦有客户端断开连接,服务器可能会丢弃那些时间戳较小的客户端的SYN包,从而导致访问服务端极其不稳定。

主机A SIP:P1 (时间戳T0) —> Server 主机A断开后

主机B SIP:P1 (时间戳T2) T2 < T0 —> Server 丢弃