前言
线上最“玄学”的一类故障,是服务本身没崩,但就是连不上:请求大量超时、偶发 connection refused、网关/应用报 cannot assign requested address,重启一会儿又好。
很多时候,这不是CPU也不是内存,而是连接资源被耗尽了——典型表现是 TIME_WAIT 爆炸、本机临时端口(ephemeral ports)耗尽、或者 conntrack 表打满。
这篇文章给你一套可落地的方法:先用最少的命令把问题定性,再把根因分到“应用/系统/网络设备”三层,最后给出一组稳妥的优化组合(含风险点)。
一、先把现象说清楚:到底“耗尽”的是什么?
常见报错大致对应三类资源:
-
端口耗尽(本机临时端口用完)
- 客户端侧常见:bind: cannot assign requested address
- 直觉:你以为是“连不上对方”,实际上是“我自己没端口了”
-
连接/文件描述符耗尽(FD不够)
- 常见:too many open files
- 直觉:进程能跑,但新连接建不起来
-
连接跟踪(conntrack)耗尽(常见于NAT/防火墙)
- 常见:转发/网关侧丢包、连接建立慢、随机超时
- 直觉:业务机器看起来正常,但“过网关就坏”
这三类问题会互相叠加,所以第一步必须定性:现在卡在谁身上。
二、三条命令快速定性(5分钟内出结论)
2.1 先看连接状态分布
ss -s
重点看:
- TCP: 总量
- estab(ESTABLISHED)是否异常高
- timewait 是否异常高
- orphaned / tw(不同系统显示略有差异)
再细一点看:
ss -tan state time-wait | wc -l
ss -tan state established | wc -l
如果 TIME-WAIT 远大于 ESTABLISHED,通常是短连接过多(或连接复用失败)引起。
2.2 确认是否是“端口耗尽”
先看本机临时端口范围:
sysctl net.ipv4.ip_local_port_range
输出类似:
net.ipv4.ip_local_port_range = 32768 60999
这意味着可用临时端口大约 28k 个。然后看 TIME_WAIT 数量是不是接近这个数量级(甚至更高)。
快速估算:如果你有 30k 左右 TIME_WAIT,同时应用还在猛烈发起新连接,端口耗尽就很容易出现。
进一步确认:抓一个典型报错进程,看它是否频繁对外建立连接:
pid=<你的进程PID>
lsof -p "$pid" -nP | grep TCP | head
如果大量是 -> 目标IP:目标端口 的 outbound 连接,而且端口持续变化,就是典型的“客户端短连接风暴”。
2.3 排除/确认 conntrack 打满(NAT/网关特别关键)
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
如果 count 接近 max,基本可以判定:conntrack 在掉链子。这类问题常见在:
- 出口 NAT 网关
- K8s 节点(iptables 模式 + 大量出向连接)
- 防火墙设备(Linux 做网关)
三、把根因分层:应用层 / 系统层 / 网络层
3.1 应用层:连接没有复用(最常见)
现象:TIME_WAIT 多、端口消耗快,但服务CPU/内存并不高。
根因通常是:
- HTTP 客户端没启用 keep-alive(或频繁创建 client)
- 连接池太小,池满后退化成短连接
- 请求超时设置不当导致连接被动关闭/频繁重建
3.1.1 Go:最容易踩的几个点
Go 的 http.Client 是可复用对象。常见错误是每次请求都 &http.Client{} 或 http.NewRequest 后不正确处理 body。
关键要点:
- 复用同一个 http.Client
- 读完并关闭 resp.Body(否则连接无法复用)
- 设置合理的连接池参数(Transport)
示例(可作为生产默认模板):
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
MaxIdleConns: 1000,
MaxIdleConnsPerHost: 200,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
client := &http.Client{
Transport: tr,
Timeout: 5 * time.Second,
}
resp, err := client.Get(url)
if err != nil { /* … */ }
defer resp.Body.Close()
io.Copy(io.Discard, resp.Body) // 读完,保证连接可复用
注意:如果你只 Close() 不读完 body,很多情况下连接还是会被丢弃,复用效果很差。
3.1.2 Java:连接池与复用要“显式正确”
以 Apache HttpClient / OkHttp 为例,核心是:
- 连接池要单例
- 超时设置要合理(connect/read/write)
- 线程数/并发量要匹配池大小
如果你用的是“每次 new 一个客户端”,TIME_WAIT 爆炸基本是必然的。
3.2 系统层:端口范围太小 / TIME_WAIT 回收策略不当
3.2.1 扩大临时端口范围
默认范围在很多高并发出向场景不够用。可以扩大到:
sysctl -w net.ipv4.ip_local_port_range="10000 65535"
持久化写入 /etc/sysctl.conf 或 /etc/sysctl.d/*.conf。
3.2.2 TIME_WAIT 的回收与复用(谨慎)
你经常会看到建议开:
sysctl -w net.ipv4.tcp_tw_reuse=1
它的含义是:在一定条件下允许复用处于 TIME_WAIT 的连接(更准确说复用本地端口)。对“客户端大量出向短连接”有帮助。
注意事项:
- tcp_tw_recycle 已经被移除(不要再找它,也不要再抄过时文章)
- tcp_tw_reuse 主要影响“主动发起连接的一端”,对纯服务端监听端口帮助有限
- 如果你的流量经过 NAT、或对端对时间戳敏感,极端情况下可能引发连接异常(虽不常见,但要灰度验证)
3.2.3 FIN/TIME_WAIT 相关参数(别乱调)
有人喜欢调:
sysctl -w net.ipv4.tcp_fin_timeout=15
这对某些场景能减少资源占用,但不是万能药,并可能影响长连接的关闭行为。建议先把“连接复用”做对,再谨慎调系统参数。
3.3 网络层:NAT/负载均衡导致端口与跟踪表压力
3.3.1 典型场景:出口 NAT 端口耗尽
当很多内网机器通过一个出口IP访问公网时,出口设备需要为每条连接分配 SNAT 端口。SNAT 端口同样是有限的。
表现:
- 内网服务访问公网不稳定、随机超时
- 出口设备 nf_conntrack 接近上限
解决思路:
- 增加出口IP(SNAT 多IP)分摊端口
- 增大 conntrack 表,并优化超时
- 降低短连接:让应用尽量长连接/连接池复用
四、从“症状 → 根因”的排查路径(建议照抄)
4.1 先确认是不是应用没复用连接
# 看某个目的端口的连接状态分布(以 443 为例)
ss -tan '( dport = :443 )' | awk 'NR>1{print $1}' | sort | uniq -c | sort -rn
# 看某个进程的对外连接数量
pid=<PID>
lsof -p "$pid" -nP | grep TCP | wc -l
如果连接建得非常快、TIME_WAIT 多、而且目的地集中在少数几个服务,优先改应用。
4.2 如果是网关/NAT,先看 conntrack
watch -n 1 'echo -n "count="; cat /proc/sys/net/netfilter/nf_conntrack_count; echo -n " max="; cat /proc/sys/net/netfilter/nf_conntrack_max'
如果 count 持续顶着 max 跑,先别在业务机上“乱调 sysctl”,根因可能在网关。
4.3 排除 FD 限制
pid=<PID>
cat /proc/"$pid"/limits | grep -i 'open files'
lsof -p "$pid" | wc -l
如果 lsof 数接近上限,先提 FD,再看连接复用。
五、落地优化方案(按优先级)
5.1 优先级A:应用侧连接复用(收益最大、风险最小)
- HTTP 客户端复用 + 连接池
- 确保响应体正确关闭/读完
- 合理的超时与重试策略(避免“超时→重连风暴”)
5.2 优先级B:扩大端口池(中等收益、低风险)
sysctl -w net.ipv4.ip_local_port_range="10000 65535"
5.3 优先级C:适度开启端口复用(需要灰度)
sysctl -w net.ipv4.tcp_tw_reuse=1
只建议在“客户端出向短连接 + 已确认复用仍不足”的场景使用,并灰度验证。
5.4 优先级D:网关/防火墙/节点 conntrack 调优(看场景)
增大表:
sysctl -w net.netfilter.nf_conntrack_max=1048576
同时要评估内存占用与超时策略(不同发行版默认值差异很大),别“只加 max 不管回收”。
六、一份速查清单(排查时直接对照)
6.1 现象 → 可能原因
| cannot assign requested address | 本机临时端口耗尽 / TIME_WAIT 太多 |
| too many open files | FD 上限不够 / 连接泄漏 |
| 随机超时、跨网段更明显 | 网关 conntrack 打满 / NAT 端口耗尽 |
| TIME_WAIT 高但服务端也有问题 | 客户端复用失败 + 服务端短连接 |
6.2 必跑命令(建议收藏)
ss -s
ss -tan state time-wait | wc -l
sysctl net.ipv4.ip_local_port_range
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
总结
连接问题排查的核心不是“调内核参数”,而是:
只要按这个顺序做,TIME_WAIT 爆炸和端口耗尽基本都能稳住,而且不会引入隐性风险。
更多运维与网络实战文章,欢迎关注公众号:北平的秋葵
网硕互联帮助中心




评论前必须登录!
注册