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 协议的正确性。

端口复用三参数关系图 端口复用 ip_local_port_range 本地临时端口范围 tcp_tw_reuse 客户端复用 TIME_WAIT tcp_tw_recycle 服务端回收 TIME_WAIT 影响:最大并发连接数 (出站方向) 影响:客户端连接复用 (安全,推荐启用) 影响:服务端连接回收 (⚠️ 高危,已废弃) 适用场景:高并发短连接、反向代理、NAT 网关 核心原则:先扩端口池,再考虑复用,绝不碰 recycle

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 时,按这个顺序排查:

  1. 先看端口池:sysctl net.ipv4.ip_local_port_range,如果范围太小,直接扩到 1024 65535
  2. 再看 TIME_WAIT 数量:ss -tan | grep TIME_WAIT | wc -l,如果超过端口池的 80%,说明需要复用
  3. 开启 tcp_tw_reuse:sysctl -w net.ipv4.tcp_tw_reuse=1
  4. 检查 tcp_tw_recycle:确保它是 0,如果是 1,立刻改回来并重启网络服务
  5. 终极手段:如果还不行,考虑调整应用层,比如使用连接池、长连接替代短连接

一个小技巧:用 cat /proc/sys/net/ipv4/ip_local_port_range 查看当前值,用 ss -tan state time-wait 看具体哪些端口被占用了。我曾经用这个组合在 5 分钟内定位了一个端口泄漏问题。

好了,这三个参数的核心内容就这些。记住一句话:先扩池,再复用,绝不回收。下一组参数我们聊聊 TCP 保活和超时相关的内核调优,那是另一个容易踩坑的地方。