背景
我司在公网环境以 docker
容器的方式搭建了开发环境的 rabbitmq
。经常听见研发的同学过来说开发环境的 rabbitmq
连接超时,管理页面打不开。自己测试了下,的确有这个情况,能不能连上好像要看运气,但是用自己的 4G 就没问题,所以一直觉得是公司网络的问题,有研发的同学找过来就让他们多试几次,着急的话先用 4G 网络。
故障排查
有天连不上的频率有点高,就打算认真研究下,彻底解决这个问题。先检查了 rabbitmq
连接数,服务器的负载,TCP 连接数,iptables
的转发策略,内核的错误日志,均没有发现异常,最后只能祭上网络问题排查的终极方案 抓包。分别在 client 端,server 端抓包,client 端抓包的结果,可以看到有大量的 TCP SYN 的重连请求,表示 client 端并没有收到 server 发来的 SYN_ACK 的包;
接着来 server 端的结果,发现 server 端其实是接受到了 client 端发送过来的 SYN 请求,但是 server 端并没有回复 SYN_ACK,这就是导致连不上的原因,server 端不回复 SYN_ACK;
通过 Google 的协助,找到了问题的原因,是设置了 Linux 内核的 net.ipv4.tcp_timestamps = 1
和 net.ipv4.tcp_tw_recycle = 1
这两个参数导致的。
net.ipv4.tcp_timestamps = 1
是启用在 TCP 包中记录 timestamp。net.ipv4.tcp_tw_recycle = 1
是开启 TCP TIME_WAIT 的快速回收。在 TCP 的规范中,处于 TIME_WAIT 状态的 TCP 必须等待 2MSL 时间才能回收,如果开启了tcp_tw_recycle
,TIME_WAIT 的 TCP 连接就不会等待 2MSL 时间,从而达到快速重用处于 TIME_WAIT 状态的 TCP 目的。
在 Linux 内核的源码中,可以发现如果同时开启了这两个 TCP 的行为,同一个 IP 在 TCP_PAWS_MSL(60s) 时间内,如果本次的连接的 timestamp 滞后于前一个连接的 timestamp, 这个连接就会丢弃。我们的使用场景正式大家在公司通过同一个 WIFI 经过路由器 NAT 去访问服务,在服务端看来就是同一个公网 IP, 在上网高峰期,由于网络的拥塞,可能会导致先发出的 TCP 包后到达服务器的情况,导致服务器不响应。
源码函数:kernel 2.6.32 tcp_v4_conn_request(),该函数是tcp层三次握手syn包的处理函数(服务端);
源码片段:
if (tmp_opt.saw_tstamp &&
tcp_death_row.sysctl_tw_recycle &&
(dst = inet_csk_route_req(sk, req)) != NULL &&
(peer = rt_get_peer((struct rtable *)dst)) != NULL &&
peer->v4daddr == saddr) {
if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
(s32)(peer->tcp_ts - req->ts_recent) >
TCP_PAWS_WINDOW) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
goto drop_and_release;
}
}
tmp_opt.saw_tstamp:该socket支持tcp_timestamp
sysctl_tw_recycle:本机系统开启tcp_tw_recycle选项
TCP_PAWS_MSL:60s,该条件判断表示该源ip的上次tcp通讯发生在60s内
TCP_PAWS_WINDOW:1,该条件判断表示该源ip的上次tcp通讯的timestamp滞后于本次tcp
可以在 server 端通过 netstat -s | grep rejected
查看到因为 timestamp
而被 rejected 掉的包。
root@dev:~# netstat -s | grep rejected
8 passive connections rejected because of timestamp
tcp_timestamps
缺省就是开启,所有,只要关闭 tcp_tw_recycle
的行为即可,通过设置内核参数 sysctl -w net.ipv4.tcp_tw_recycle=0
解决问题。
总结
在回顾这次的故障时发现之前也遇到过同样的问题,之前的情况是 GitLab 在公司一会能打开一会打不开,也是设置了开启了 TCP TIME_WITE 快速回收导致,还写过一篇博客记录了下,然而事实告诉我并没什么卵用啊,想了下可能跟文章题目有关,之前的题目叫做 TCP优化net.ipv4.tcp_tw_recycle踩的坑 ,这次干脆来个 修复一例因错误设置tcp_tw_recycle导致服务一会能访问一会不能访问故障,再应该会记住了 😄。