问题描述:
外网云服务器上部署了一个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服务,也出现了“响应时间过长”的现象,如下图所示:
同时通过SSH连接云服务器时,也返回了“Connection failed”错误。
这些问题持续不到1分钟之后就又正常了。
分析过程:
1、怀疑网络有丢包。
在又出现此问题时,分别在公司内网主机和云服务器上执行mtr观察。发现两边都没有丢包现象,这个怀疑暂时排除掉了。mtr结果如下:
2、怀疑当时流量较高,带宽被占满。
查看了监控端的流量图表,发现流量曲线不稳定,有流量达到瓶颈的现象,于是在云服务器上使用iftop命令(iftop -p)实时查看了流量情况。
当又出现问题时,流量也很平稳,这个怀疑点也被排除了。流量信息如下图:
3、怀疑当时的负载较高。
通过dstat实时查看负载,又出现问题时,各项指标都很正常。dstat部分结果如下:
4、怀疑是可使用的文件描述符达到了限制。
在云服务器上查看之后,也排除了。结果如下图:
5、会是防火墙drop了吗?
云服务器上运行的Docker启用了iptables,除此之外没有其他规则了。
6、查看sysctl.conf文件
文件中的几个参数看起来没有太大问题。
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部分如图:
结果说明:公司内网主机向云服务器发送了SYN,但是云服务器没有返回ACK,内网主机在等待超时后重新发送了SYN。等待超时基于重新发送超时时间(RTO, retransmission timeout),在Wireshark中显示为“TCP Retransmission”。
乱序片段如图:
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