tcp_tw_recycle引发的间断性服务无法访问

问题描述:

外网云服务器上部署了一个RabbitMQ节点,在公司内网调用RabbitMQ接口时经常返回以下错误:

panic: dial tcp 123.1.1.1:5672: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

访问ECS上的HTTP服务,也出现了“响应时间过长”的现象,如下图所示:

spacer.gif无法访问网站.png

同时通过SSH连接云服务器时,也返回了“Connection failed”错误。

这些问题持续不到1分钟之后就又正常了。



分析过程:

1、怀疑网络有丢包。

在又出现此问题时,分别在公司内网主机和云服务器上执行mtr观察。发现两边都没有丢包现象,这个怀疑暂时排除掉了。mtr结果如下:

spacer.gif云服务器mtr.png

内网mtr.png

spacer.gif

2、怀疑当时流量较高,带宽被占满。

查看了监控端的流量图表,发现流量曲线不稳定,有流量达到瓶颈的现象,于是在云服务器上使用iftop命令(iftop -p)实时查看了流量情况。

当又出现问题时,流量也很平稳,这个怀疑点也被排除了。流量信息如下图:

spacer.gif实时流量.png


3、怀疑当时的负载较高。

通过dstat实时查看负载,又出现问题时,各项指标都很正常。dstat部分结果如下:

spacer.gifdstat.png


4、怀疑是可使用的文件描述符达到了限制。

在云服务器上查看之后,也排除了。结果如下图:

spacer.gifulimit.png


5、会是防火墙drop了吗?

云服务器上运行的Docker启用了iptables,除此之外没有其他规则了。

spacer.gifiptables.png


6、查看sysctl.conf文件

文件中的几个参数看起来没有太大问题。

spacer.gifsysctl.png


7、考虑不同的网络环境

把一个HTTP链接,发给了其他公司工作的同事。当问题又出现时,他在公司内部可以正常访问,同时,我在手机上用4G也可以正常访问。


8、ARP欺骗

能想到的原因都过了一遍,结论是“我公司网络到云服务器的网络有问题”。最初想到了ARP欺骗。

查看云服务器MAC地址:

[root@hadoop001 ~]# cat /sys/class/net/eth0/address

8.1、同时在公司内网主机和云服务器上启用tcpdump进行抓包。

其中,在云服务器上执行的tcpdump命令如下(111开头的地址是公司出口IP):

tcpdump -nn tcp port 15672 or tcp port 22 and host 111.*.*.* -w server.pcap

8.2、在问题出现时,我重复点了几次RabbitMQ的Web管理控制页面,并重试了几次SSH。

8.3、用Wireshark打开下载pcap文件,对比了抓到的包中云服务器的MAC地址和在服务器上拿到的MAC地址,结果是一样的。


9、抓包结果中大量的RTO和乱序片段

RTO部分如图:

spacer.gifRTO.png

结果说明:公司内网主机向云服务器发送了SYN,但是云服务器没有返回ACK,内网主机在等待超时后重新发送了SYN。等待超时基于重新发送超时时间(RTO, retransmission timeout),在Wireshark中显示为“TCP Retransmission”。

乱序片段如图:

spacer.gifooo.png

TCP Dup ACK:代表数据段丢失,重复ACK。图中的[TCP Dup ACK 383#1]指数据段丢失的位置是383,为第一次丢失。

TCP Out-Of-Order:收到的数据包乱序。


10、最后的分析

TCP Out-Of-Order和TCP Dup ACK,常见于网络拥堵,延迟增大的情况下,后发送的数据包可能先到达。但是结合之前的mtr和iftop,表明公司内网主机和云服务器之间不存在网络拥堵的情况。

到这里,事情已经明朗了,云服务器上将内核参数“tcp_timestamps”置为1,检查客户端每个数据包的时间戳是否递增。当客户端处在一个NAT环境时,共用同一个出口IP。因为每个PC机上的时间不一定一致,不一定都准确。有可能导致从出口IP发往云服务器的数据包,时间戳是混乱的,云服务器检查数据包时表现为时间戳不是递增的。

单独启用“tcp_timestamps”时,通常是没有问题的,处于TIME_WAIT状态的TCP连接等待2MSL之后,会被正常回收。而如果服务器同时将“tcp_tw_recycle”置为1,就可能出现问题了。

将“tcp_tw_recyle”参数置为1时,服务器会在一个小于2MSL的极短时间(2RTT)内回收处于TIME_WAIT状态的TCP连接。由于已经释放了socket,服务器无法用socket来标记一个连接,只能通过数据包中的时间戳来判断是不是新的数据包,如果不是则丢弃。结果就是客户端一直在重传SYN,服务器不返回ACK。

11、处理方式

修改内核配置文件将以上内核参数值都修改为0 :

vim /etc/sysctl.conf 
net.ipv4.tcp_tw_recycle=0 
net.ipv4.tcp_timestamps=0 
执行如下命令使修改的配置生效:
sysctl -p


猜你喜欢

转载自blog.51cto.com/13568014/2497459