一次由 tcp_tw_recycle 引发的“灵异”故障排查:基于双网闸架构的踩坑记

一次由 tcp_tw_recycle 引发的"灵异"故障排查:基于双网闸架构的踩坑记

背景

最近在排查一个线上服务的网络连接问题,现象非常"诡异":

  • 服务端和客户端逻辑都没有明显改动。
  • 大部分请求正常,但偶尔会出现连接建立失败、超时。
  • 抓包发现 TCP 握手阶段有异常,部分 SYN 包被直接丢弃,没有回应。
  • 问题不是必现,但一到业务高峰期就零星出现,让人非常头疼。

经过一系列排查,最终定位到是 Linux 老版本内核参数 tcp_tw_recycle 在作祟。修改后,问题立刻消失。

这篇文章记录一下整个排查思路和背后的原理,希望能帮到遇到类似问题的同学。

网络拓扑与问题现象

我们的网络架构比较特殊,采用了双网闸(安全隔离与信息交换系统) 进行内外网隔离,拓扑简化如下:

  • 内网核心 :T:1/3/0/42, VLAN3102,IP 10.10.10.1/24
  • 内-外网闸 (172.16.1.156):
    • 内网业务口 t1/17:10.10.10.2/30
    • 外网链接发起口 t1/17:10.10.20.2/24
  • 外网 AF :
    • eth18:10.10.20.1/24
    • eth19:10.10.30.1/30
  • 外-内网闸 (172.16.1.157):
    • 外网业务口 t1/17:10.10.30.2/30
    • 内网链接发起口 t1/17下:10.10.10.3/24

数据流向 :

内网核心(10.10.10.1)→ 内-外网闸(10.10.20.2)→ 外网 AF(10.10.20.1)→ 外网 AF(10.10.30.1)→ 外-内网闸(10.10.30.2)→ 内-外网闸(10.10.10.2)→ 内网核心(10.10.10.1)。

故障现象 :

在这种双网闸、多段 NAT 的架构下,内网核心访问外网业务时,部分连接会卡在 SYN 阶段,服务端没有回应 SYN-ACK,也没有 RST。客户端重试几次后,部分连接成功,部分彻底失败。

排查过程

1. 初步怀疑:网络抖动?服务端负载?

一开始怀疑是网络抖动或者服务端负载过高。但查看监控:

  • 服务端 CPU、内存、带宽都正常。
  • 网闸设备没有丢包告警。
  • 问题出现在多个客户端,但又不是全部客户端。

2. 抓包分析:SYN 包被丢弃

在内网核心 (10.10.10.1)和外网 AF(10.10.20.1)同时抓包,发现一个关键现象:

  • 内网核心发出 SYN 包。
  • 经过内-外网闸后,源 IP 被 NAT 成 10.10.20.2。
  • 外网 AF 收到 SYN 包,但没有回应 SYN-ACK。
  • 内网核心重试几次后,部分连接成功,部分彻底失败。

这看起来像是外网 AF 或外-内网闸直接把 SYN 包丢了,而不是应用层的问题。

3. 关键验证:关闭 TCP 时间戳后恢复正常

在排查过程中,发现了一个非常关键的验证现象:

  • 如果把内网所有访问该外网服务器的客户端 的 TCP 时间戳(tcp_timestamps)全部关闭,连接完全正常。
  • 但只要其中任意一台服务器重新开启时间戳访问,问题就会再次出现。

这个现象几乎直接锁定了问题根源:不是网络设备丢包,而是服务端内核根据 TCP 时间戳做了某种"过滤"。

4. 聚焦内核参数:tcp_tw_recycle

查阅资料后,怀疑到 tcp_tw_recycle 这个参数。

先确认当前内核版本和参数值:

bash 复制代码
uname -r
# 输出:3.10.0-xxx.el7.x86_64 (老版本内核)

sysctl net.ipv4.tcp_tw_recycle
# 输出:net.ipv4.tcp_tw_recycle = 1

果然,tcp_tw_recycle 被开启了。

什么是 tcp_tw_recycle?

tcp_tw_recycle 是 Linux 内核的一个参数,用于快速回收 TIME_WAIT 状态的 socket。

  • 默认值:0(关闭)

  • 开启后:内核会使用更激进的策略回收 TIME_WAIT,减少端口占用。

听起来很美好,但它有一个致命缺陷:

在 NAT 环境下,tcp_tw_recycle 会导致部分客户端连接被丢弃。

为什么 NAT 环境下会出问题?

  • tcp_tw_recycle 依赖 TCP 时间戳(tcp_timestamps) 来判断连接的新旧。

开启 tcp_tw_recycle 后,内核会记录每个源 IP 的最近时间戳(per-host PAWS)。

  • 如果同一个源 IP 发来的新连接,时间戳 小于 之前记录的最近时间戳,内核会认为这是一个"过时"的包,直接丢弃。

问题来了:

在 NAT 环境下,多个客户端共享同一个源 IP(对服务端而言)。

不同客户端的系统时间戳(TCP TSval)可能不同步(有的快,有的慢)。

如果客户端 A 的时间戳较大,客户端 B 的时间戳较小,B 的 SYN 包就会被服务端判定为"过时",直接丢弃。

在我们的拓扑中:

内网核心(10.10.10.1)经过内-外网闸后,源 IP 被 NAT 成 10.10.20.2。外网 AF(10.10.20.1)作为服务端,看到的所有连接都来自 10.10.20.2。如果内网有多台服务器(或同一客户端多次连接),只要其中一台开启了 TCP 时间戳,并且它的 TSval 比之前记录的更大,就会拉高服务端为 10.10.20.2 维护的"最近时间戳"。之后其他时间戳较小的客户端再发起连接,就会被直接丢弃。

这就是为什么:

问题不是必现,而是部分客户端、部分时间出现。

全部关闭时间戳就正常(服务端不再做 PAWS 校验)。

只要有一台开启时间戳就会出问题(它会"污染"服务端为该 NAT 源 IP 维护的时间戳水位)。

解决方案

修改内核参数,关闭 tcp_tw_recycle:

bash 复制代码
sysctl -w net.ipv4.tcp_tw_recycle=0
永久生效:
bash 复制代码
echo "net.ipv4.tcp_tw_recycle = 0" >> /etc/sysctl.conf
sysctl -p
修改后,问题立刻消失,连接成功率恢复正常。
  • 注意:关闭 tcp_tw_recycle 是根治方案。关闭客户端时间戳只是临时规避手段,且会带来其他副作用(如无法使用 RTTM、PAWS 保护减弱等),不建议作为长期方案。

为什么老版本内核更需要注意?

在 Linux 4.12 之后,tcp_tw_recycle 已经被彻底移除。

但在 CentOS 7、早期 Ubuntu 等老版本内核中,这个参数依然存在,且可能被一些"优化教程"建议开启。

很多运维人员为了减少 TIME_WAIT,会盲目开启 tcp_tw_recycle,结果在 NAT 环境下踩坑。

经验总结

不要盲目开启 tcp_tw_recycle,尤其是在 NAT 环境下。

如果必须优化 TIME_WAIT,优先考虑:

开启 tcp_tw_reuse(相对安全)

调整 tcp_max_tw_buckets

使用长连接

老版本内核升级前,务必检查这个参数。

排查网络问题时,抓包 + 内核参数检查是黄金组合。

在双网闸、多段 NAT 架构下,尤其要注意 TCP 时间戳和 tcp_tw_recycle 的相互作用。

"关闭所有客户端时间戳就正常,开启任意一台就复现" 是 tcp_tw_recycle 在 NAT 环境下引发问题的典型特征,可作为快速定位依据。

  • 参考
    Linux 内核文档:Documentation/networking/ip-sysctl.txt
    RFC 1323:TCP Extensions for High Performance
    关于 tcp_tw_recycle 被移除的 commit
相关推荐
爱写代码的倒霉蛋1 小时前
我用book-to-skill把尹圣雨的《TCP/IP网络编程》做成了知识库(适合快速入门网络编程)
网络·网络编程·知识库
..过云雨1 小时前
19.【网络】多路转接poll、epoll、Reactor反应堆 - 修正版
网络
滑翔的企鹅2 小时前
从用户需求看 CMS:同一份内容如何服务网页、APP、搜索与 AI?
架构
Raas1002 小时前
MAI Gateway(魔芋企业级AI网关)功能全解:AI网关能做Prompt注入防护吗?一文看懂AI网关能力矩阵(v1.2.0)
网络·人工智能·gateway·prompt·mai gateway·企业级ai网关·企业级产品
Lzg_na2 小时前
DHCP 作用、原理、机制和报文格式详解
网络
姜鱼问生2 小时前
Docker Volume 备份与恢复实战:不停机迁移数据
后端·算法·架构
姜鱼问生2 小时前
服务器磁盘满了?从告警到清理的完整排查
后端·算法·架构
发量惊人的中年网工2 小时前
SD-WAN 组网方案怎么选?跨地域与多分支企业的选型指南(含成本对比)
服务器·网络·数据库
星恒讯工业路由器3 小时前
从5G-A到6G:工业通信网络的技术演进与产业落地
网络·物联网·5g·智能路由器·工业路由器·工业物联网·5g-a