4. Linux 内核参数:端口复用三剑客
聊到端口复用,绕不开 Linux 内核的三个关键参数。说实话,我见过不少运维同事,一遇到端口不够用就盲目调大 ip_local_port_range,或者听说 tcp_tw_recycle 能回收连接就无脑开启。结果呢?要么问题没解决,要么引入了更隐蔽的坑。
今天我就把这哥仨掰开揉碎了讲清楚。它们分别是:
- net.ipv4.ip_local_port_range —— 本地端口池的大小
- net.ipv4.tcp_tw_reuse —— 客户端视角的 TIME_WAIT 复用
- net.ipv4.tcp_tw_recycle —— 服务端视角的 TIME_WAIT 回收(⚠️ 高危)
一句话总结:ip_local_port_range 决定你能发起多少新连接;tcp_tw_reuse 让客户端安全复用 TIME_WAIT 状态的端口;tcp_tw_recycle 已被 Linux 4.12+ 彻底移除,因为它破坏了 TCP 协议的正确性。
4.1 net.ipv4.ip_local_port_range —— 你的端口池有多大?
这个参数定义了系统为主动发起 TCP 连接分配的临时端口范围。说白了,你的机器作为客户端去连别人时,内核就从这里拿一个端口用。
默认值通常是 32768 60999,一共 28232 个端口。听起来不少?我跟你算笔账:
- 如果你的应用每秒发起 1000 个短连接,每个连接在 TIME_WAIT 状态停留 60 秒
- 那么同时处于 TIME_WAIT 的连接数就是 1000 × 60 = 60000
- 端口池只有 28232 个,根本不够用!
这时候你就会看到 Cannot assign requested address 错误。嗯,我在调优 Nginx 反向代理时遇到过好几次,客户端日志里全是这个报错。
我的建议:对于高并发出站代理服务器,直接把范围改成 1024 65535。这样可用端口数从 2.8 万提升到 6.4 万,翻了一倍多。
# 查看当前值
sysctl net.ipv4.ip_local_port_range
# 临时修改
echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range
# 永久生效(写入 /etc/sysctl.conf)
net.ipv4.ip_local_port_range = 1024 65535
sysctl -p
但注意,别把起始值设得太低(比如 1024 以下)。1024 以内的端口是特权端口,普通用户进程用不了。另外,如果你机器上还有大量服务在监听固定端口,也要避开这些端口范围。
4.2 net.ipv4.tcp_tw_reuse —— 客户端的安全复用
这个参数的作用是:允许客户端在发起新连接时,复用还在 TIME_WAIT 状态的端口。前提是内核能确认新连接的序列号比旧连接大,不会造成数据混淆。
说白了,它是个「聪明」的复用机制。内核会检查旧连接的 TIME_WAIT 是否已经过了足够长的时间(至少 1 个 MSL,通常 2 秒),确保网络中不会有旧连接的残留包。
适用场景:客户端主动发起大量短连接,比如反向代理、HTTP 客户端、数据库连接池。我自己的经验是,在 Nginx 反向代理场景下开启 tcp_tw_reuse,配合 ip_local_port_range 扩池,基本能解决 90% 的端口耗尽问题。
# 开启 tcp_tw_reuse
sysctl -w net.ipv4.tcp_tw_reuse=1
# 永久配置
echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf
sysctl -p
注意:tcp_tw_reuse 只对 出站连接(客户端角色)有效。如果你的服务器作为服务端接受连接,这个参数帮不上忙。服务端 TIME_WAIT 过多,得靠其他手段解决。
4.3 net.ipv4.tcp_tw_recycle —— 一个已被历史淘汰的参数
这个参数曾经被很多人当作「救星」。它允许服务端更快地回收 TIME_WAIT 状态的连接,把等待时间从 60 秒缩短到 RTO(重传超时时间,通常几秒)。
但问题来了——它依赖 TCP 时间戳选项,而且对 NAT 环境有严重破坏性。我记得 2015 年左右,有个客户反馈他们的移动端用户频繁出现连接失败,查了半天,最后发现是某台 LVS 上开了 tcp_tw_recycle。NAT 后面的不同客户端共享同一个公网 IP,但时间戳不同步,导致内核错误地丢弃了合法包。
避坑指南:我曾经在线上环境吃过这个亏。从那以后,我所有服务器的 sysctl 配置里都明确写死了 net.ipv4.tcp_tw_recycle = 0。而且从 Linux 4.12 开始,这个参数已经被彻底移除,你写 1 也没用了。
如果你还在用老内核(比如 CentOS 6/7 的 3.x 内核),千万别碰它。正确的做法是:
- 服务端 TIME_WAIT 过多 → 用
SO_LINGER或调整应用层逻辑 - 客户端 TIME_WAIT 过多 → 用
tcp_tw_reuse+ 扩端口池 - 实在不行 → 考虑
tcp_tw_reuse+tcp_timestamps组合
4.4 三个参数的对比总结
| 参数 | 作用方向 | 安全性 | 推荐度 | 适用内核版本 |
|---|---|---|---|---|
ip_local_port_range |
出站(客户端) | 完全安全 | ⭐⭐⭐⭐⭐ | 所有版本 |
tcp_tw_reuse |
出站(客户端) | 安全(需时间戳) | ⭐⭐⭐⭐ | 所有版本 |
tcp_tw_recycle |
入站(服务端) | 高危(NAT 破坏) | ❌ 废弃 | 4.12 以下(不推荐) |
4.5 实战排错流程
当你遇到 Cannot assign requested address 或 Too many open files 时,按这个顺序排查:
- 先看端口池:
sysctl net.ipv4.ip_local_port_range,如果范围太小,直接扩到1024 65535 - 再看 TIME_WAIT 数量:
ss -tan | grep TIME_WAIT | wc -l,如果超过端口池的 80%,说明需要复用 - 开启 tcp_tw_reuse:
sysctl -w net.ipv4.tcp_tw_reuse=1 - 检查 tcp_tw_recycle:确保它是 0,如果是 1,立刻改回来并重启网络服务
- 终极手段:如果还不行,考虑调整应用层,比如使用连接池、长连接替代短连接
一个小技巧:用 cat /proc/sys/net/ipv4/ip_local_port_range 查看当前值,用 ss -tan state time-wait 看具体哪些端口被占用了。我曾经用这个组合在 5 分钟内定位了一个端口泄漏问题。
好了,这三个参数的核心内容就这些。记住一句话:先扩池,再复用,绝不回收。下一组参数我们聊聊 TCP 保活和超时相关的内核调优,那是另一个容易踩坑的地方。