修复一例因错误设置tcp_tw_recycle导致服务一会能访问一会不能访问故障

背景

我司在公网环境以 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 = 1net.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导致服务一会能访问一会不能访问故障,再应该会记住了 。

你可能感兴趣的:(修复一例因错误设置tcp_tw_recycle导致服务一会能访问一会不能访问故障)