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