TIME_WAIT 堆到几万条:先分清正常回收、端口耗尽和连接泄漏
TIME_WAIT 是正常状态,不是错误
TCP 连接关闭时,主动关闭的一方会进入 TIME_WAIT,并保持 2MSL(Linux 上约 60 秒)。目的是两件事:让最后那个 ACK 有机会重传,以及让这条四元组的旧报文在网络里自然消亡,不至于串到之后复用的同号连接上。
所以看到 TIME_WAIT 先别慌,它说明连接被正常关闭了。真正要判断的是:数量是否超过了「正常回收速度」,以及有没有影响到新建连接。
第一步:看数量和分布
ss -s
# TCP: 31204 (estab 412, closed 30512, orphaned 0, timewait 30480)
ss -tan state time-wait | wc -l
# 按本地端口统计,找出谁在疯狂建连
ss -tan state time-wait | awk 'NR>1 {print $3}' | cut -d: -f2 | sort | uniq -c | sort -rn | head
如果 TIME_WAIT 集中在少数几个本地端口上,通常是这些端口连接同一个后端、短连接调用太频繁;如果分散在大量高位端口上,更像是本机作为客户端在不停向外发起连接。
第二步:确认是不是端口耗尽
客户端的可用端口由这两项决定:
sysctl net.ipv4.ip_local_port_range
# net.ipv4.ip_local_port_range = 32768 60999
cat /proc/sys/net/ipv4/tcp_tw_reuse
默认范围约 2.8 万个端口。如果同一时间对同一个「目的 IP:端口」的短连接速率很高,四元组会被 TIME_WAIT 占满,新建连接就会报:
connect: Cannot assign requested address
注意这个报错也可能来自文件描述符耗尽或本地端口被占满,配合 ss -s 的 timewait 数量和 ss -tan state time-wait '( dport = :443 )' 一起看更准确。
第三步:算一下是不是真的异常
TIME_WAIT 的稳态数量大致等于:每秒新建连接数 × 60 秒。每秒 500 个短连接,稳态就是 3 万条,属实现象;每秒 5 个连接却堆到 3 万,才说明有连接没被正常回收,或者有人在扫端口。
另外要分清方向:只有主动关闭方才有 TIME_WAIT。如果你的服务是 HTTP 服务端、由客户端先断开,那服务端上大量 TIME_WAIT 反而说明是它主动关的连接(比如 keepalive 超时后服务端先关),需要看 keepalive_timeout 的配置。
能做什么,以及副作用
- 连接复用优先:HTTP 开 keep-alive、数据库用连接池、gRPC 长连接。把每秒 500 次短连接降到 50 次,TIME_WAIT 自然降一个数量级,这是唯一没有副作用的做法。
- tcp_tw_reuse=1:允许把处于 TIME_WAIT 的连接(带时间戳、且新序列号更大)复用于出站连接。对客户端有效,对被动关闭的服务端没帮助;依赖
tcp_timestamps=1。 - 扩大 ip_local_port_range:比如改成
10240 65535,只是把上限抬高,治标;范围越大,和已有服务端口冲突的概率也越高。 - 不要用 tcp_tw_recycle:从 Linux 4.12 起已被移除,早年开启它在 NAT 环境会导致大量连接随机失败。
- SO_REUSEADDR:只影响 bind 时能否复用地址,和 TIME_WAIT 的回收不是一回事,别指望它解决端口耗尽。
什么时候可以完全不管
如果 ss -s 里 estab 数量稳定、新建连接成功率正常、没有 Cannot assign requested address,那 TIME_WAIT 几万条只是繁忙的证据,不需要调参。贸然把 tcp_tw_reuse 打开、或把 2MSL 改短,反而可能引入更难查的问题。
监控上建议盯三个量:TIME_WAIT 总数、estab 数量、以及连接建立失败的计数(netstat -s | grep -i "failed\|overflow")。只看第一个数字,很容易被正常值吓到。
- 点赞
- 收藏
- 关注作者
评论(0)