1. 问题背景与现象分析
最近在排查一个典型的网络调用超时问题,这个问题的表现非常有意思:当系统空闲一段时间后,首次请求经常会出现超时,但重试又能成功。具体特征如下:
- 偶发性超时:主要发生在流量较小的场景,特别是长时间空闲后的首次请求
- 超时时间无效:即使将超时时间设置为1分钟,仍然会超时(正常请求1秒内返回)
- 重试成功:超时后的重试调用通常都能成功
- 局部性问题:同一时间其他相同调用不受影响
- 网络环境相关:内网调用不会出现,专线或公网容易出现
- 服务端无记录:服务端日志完全找不到超时请求的痕迹
这种问题在分布式系统中相当常见,但排查起来往往让人头疼。下面我将详细记录我的排查思路和最终解决方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 初步猜想与验证
2.1 猜想一:服务端主动关闭连接
第一个想到的可能性是服务端主动关闭了连接。但通过分析TCP协议行为,很快排除了这个可能性:
TCP连接关闭有两种情况:
- 客户端未收到FIN包:请求发送后会直接收到Reset,立即报错
- 客户端收到FIN包:请求发送前就会报"socket already closed"错误
这两种情况都会立即返回错误,而不会等待很长时间后超时,与观察到的现象不符。
2.2 猜想二:路由翻动问题
考虑到超时时间很长,我开始怀疑是网络层丢包或路由问题。但路由翻动通常会导致分钟级的影响,且会同时影响所有请求。而我们的现象是:
- 重试后立即成功(5秒内)
- 其他并发请求不受影响
这些特征与路由问题不符,因此也排除了这个可能性。
3. 根本原因分析
3.1 NAT表项超时机制
问题的真正原因与NAT(网络地址转换)机制有关。由于IPv4地址短缺,NAT技术被广泛使用,但它本质上是个"补丁",会带来一些副作用:
- 转发表资源有限:NAT设备需要维护内网IP:Port与外网IP:Port的映射表
- 超时清理机制:长时间无流量的连接会被清理以释放资源
- 两端无感知:客户端和服务端无法感知NAT表项被清除
当NAT表项被清除后,客户端发出的包无法正确转发,经过多次重传后最终超时。这完美解释了所有观察到的现象:
- 空闲后首次请求超时(表项被
