tcp-time-wait

ss 显示大量 TIME_WAIT,客户端偶发 Cannot assign requested address 或 conntrack 表压力。

现象

高 QPS 短连接服务出现连接失败、超时或端口耗尽告警;ss -s 和 ss -tan state time-wait 显示大量 TIME_WAIT。服务端本身可能正常,问题集中在上游主动关闭连接的一方。

快速定位

先确认 TIME_WAIT 在哪一侧、哪个端口,再看临时端口和 conntrack:

ss -tan state time-wait | wc -l
ss -tan state time-wait | awk '{print $4}' | sed 's/.*://' | sort | uniq -c | sort -nr | head
ss -tan state time-wait '( sport = :8080 )' | head
cat /proc/sys/net/ipv4/ip_local_port_range
sysctl net.ipv4.tcp_tw_reuse net.ipv4.tcp_max_tw_buckets net.ipv4.tcp_fin_timeout
ss -s
cat /proc/net/sockstat

连接表与错误日志:

cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
dmesg | grep -i 'conntrack.*table full'
grep -R 'Cannot assign requested address' /var/log/ 2>/dev/null | tail
netstat -s | grep -iE 'time wait|overflow'

TIME_WAIT 出现在哪一端,说明哪一端先发送了第一个 FIN。要确认关闭方向,可用抓包看 FIN:

tcpdump -i any -nn 'tcp port 8080 and (tcp[tcpflags] & (tcp-fin|tcp-rst) != 0)'

易误判:TIME_WAIT 是正常的连接状态(Linux 固定约 60 秒,即 2×MSL),数量高本身不是故障;真正的信号是 Cannot assign requested address、conntrack 满或四元组冲突。tcp_fin_timeout 不影响 TIME_WAIT(它控制 FIN_WAIT_2),改它不会减少 TIME_WAIT。tcp_tw_recycle 在 Linux 4.12 已移除,且在 NAT 环境下有害,禁止使用。

常见根因

  1. 高 QPS 短连接、无连接池,客户端主动关闭后留下大量 TIME_WAIT。
  2. 临时端口范围太小(默认约 2.8 万个),高并发出站连接不够用。
  3. 服务端主动关闭(Connection: close、keepalive 关闭、HTTP/1.0),TIME_WAIT 落在服务端。
  4. conntrack/NAT 表被短流或 TIME_WAIT 占满,开始丢包。
  5. tcp_max_tw_buckets 太小触发溢出,内核提前清理 TIME_WAIT。
  6. 旧内核误开 tcp_tw_recycle,NAT 后客户端连接随机失败。
  7. 客户端与服务端 keepalive 不匹配,连接频繁重建。
  8. 健康检查、扫描或重试策略造成大量短连接。

修复

相关命令

读完自测

  1. TIME_WAIT 数量高是否一定需要处理?
  2. 调小 tcp_fin_timeout 能缩短 TIME_WAIT 吗?
  3. tcp_tw_reuse 能解决服务端 TIME_WAIT 过多的问题吗?
答案
  1. 不一定;它是正常状态,只有出现端口耗尽、conntrack 满或连接失败时才需要处理。
  2. 不能;tcp_fin_timeout 控制 FIN_WAIT_2,TIME_WAIT 时长由内核固定。
  3. 不能;tcp_tw_reuse 只对主动发起连接的一方生效,不解决服务端被动关闭产生的 TIME_WAIT。