朕喜技术(ZhenxiJS)
2026.08.28
1 分钟阅读

实战:TCP TIME_WAIT 连接堆积导致端口耗尽(Cannot assign requested address)

问题背景

高频 API 服务用短连接(每次请求新建 TCP 连接、用完即关)访问后端。上线新版本后服务大面积超时, 客户端大量报错:

OSError: [Errno 99] Cannot assign requested address

同时:后端 CPU/内存/磁盘/网络均正常、服务端口能 telnet 通,但新建连接就是失败。 ss -tan 显示高达 2.8 万个 TIME_WAIT,而 ip_local_port_range 只有 100 个端口 —— 典型的 TCP TIME_WAIT 堆积导致的本地临时端口耗尽。

复现步骤

cd scenario/2026-08-28-0800/
docker compose up -d --build
# 受限端口范围 + 关闭 reuse,精确复现(容器 net sysctl 只读时,用 docker run --sysctl 建独立 netns)
docker run --rm -d --name repro005 --network 2026-08-28-0800_default \
  --sysctl net.ipv4.ip_local_port_range="20000 20099" \
  --sysctl net.ipv4.tcp_tw_reuse=0 \
  --cap-add NET_ADMIN -v "$PWD/../..:/workspace" 3day-scenario-005:latest sleep 600
docker exec repro005 bash -c "cd /workspace/scenario/2026-08-28-0800 && python3 server.py & sleep 1 && python3 client.py 300 -v"

复现成功:成功=128, 失败=172,新连接报 OSError: [Errno 99] Cannot assign requested address。

根本原因

TCP 四次挥手中,主动关闭方收到最后一个 ACK 后进入 TIME_WAIT,等待 2×MSL(默认 60s)才释放。

  • 短连接高频场景下,客户端每个连接用完即关 → 每次都产生一个 TIME_WAIT 并堆积;
  • 系统可用源端口范围有限(本例仅 100 个),很快被 TIME_WAIT 占满;
  • 新连接尝试分配本地源端口时失败 → Cannot assign requested address。

解决办法(均已在容器内验证)

方案命令/做法效果
内核 tcp_tw_reuse=1sysctl -w net.ipv4.tcp_tw_reuse=1✅ 300/300 成功,复用 TIME_WAIT 端口
扩大端口范围sysctl -w net.ipv4.ip_local_port_range="10000 60000"✅ 300/300 成功(治标)
调小 tcp_fin_timeoutsysctl -w net.ipv4.tcp_fin_timeout=5❌ 单独用仍 181 失败,只加速回收
连接池/keep-alive 长连接应用层复用连接✅ 最佳,300/300 成功,TIME_WAIT 仅 6

推荐:应用层连接池 / keep-alive 长连接(治本),必要时配合 tcp_tw_reuse。 tcp_tw_recycle 在新内核已移除且会破坏 NAT,不要用;SO_LINGER 强制 RST 有丢数据风险,谨慎。

验证

  • 方案1/3/4 下:client.py 300 全部成功、ss -tan state time-wait | wc -l 不再挤爆端口、新连接可建。
  • 方案4(5 条长连接承载 300 请求):TIME_WAIT=6,几乎为零。

经验与避坑

  1. 诊断三板斧:ss -tan state time-wait | wc -l 看堆积、cat /proc/sys/net/ipv4/ip_local_port_range 看可用端口、对比“TIME_WAIT 数量 vs 可用端口数”即可定位根因。
  2. Docker 容器改 net sysctl 的坑:若容器与宿主机共享网络命名空间,/proc/sys/net/ipv4/... 和 sysctl -w 运行时都是只读的;unshare --net 也被禁止。要用 docker run --sysctl 在创建时注入, 或起独立网络命名空间的容器。且 --sysctl 只在创建时生效,运行时再改仍会被拒。
  3. tcp_tw_reuse 依赖 TCP 时间戳(PAWS),同一 4 元组才可复用,跨不同目标地址无法复用。
  4. 连接池方案要求服务端也支持 keep-alive(HTTP/1.1 默认),否则单连接无法承载多请求。
  5. 短连接高频业务(微服务、API 网关、支付回调)应优先用连接池/HTTP 长连接,其次才是调内核参数。

技术栈:TCP/IP、TIME_WAIT、socket 编程、Docker/网络命名空间、sysctl、ss/netstat。

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-08-28-0800-tcp-timewait-port-exhaustion

分享这篇文章