4、Linux 内核参数调优:net.ipv4.tcp_tw_reuse、net.ipv4.tcp_tw_recycle、net.ipv4.tcp_fin_timeout 配置

聊到端口复用,绕不开这三个内核参数。说实话,我见过不少运维同事,一上来就把这三个参数全打开,觉得「开了总比不开好」。结果呢?业务出问题了,查半天才发现是参数配错了。

今天咱们就把这三个参数掰开揉碎,讲清楚它们各自管什么、怎么配、有什么坑。

4.1 先搞清楚 TIME_WAIT 是怎么来的

在讲参数之前,得先明白一个问题:为什么会有 TIME_WAIT 状态?

TCP 四次挥手结束后,主动关闭连接的一方会进入 TIME_WAIT,默认等 2MSL(Linux 上固定 60 秒)。为什么要等?两个原因:

  • 保证最后一个 ACK 能到达对端,万一丢了,对端会重发 FIN
  • 让旧连接的所有报文在网络中消失,避免干扰新连接

但问题来了——高并发短连接场景下,服务器作为主动关闭方,会积累大量 TIME_WAIT 套接字。每个套接字占一小点内存,端口也被占着。端口耗尽时,新连接就建立不了了。

核心矛盾:TIME_WAIT 是 TCP 可靠性的保障,但太多又会拖垮性能。这三个参数就是用来解决这个矛盾的。

4.2 net.ipv4.tcp_tw_reuse —— 我比较喜欢的一个参数

这个参数的意思是:允许将 TIME_WAIT 状态的 socket 用于新的 TCP 连接。注意,是「用于新的连接」,不是「回收」。

它的工作原理是这样的:当内核分配端口时,如果所有可用端口都用完了,它会去检查 TIME_WAIT 状态的 socket。如果那个 socket 的 timestamp 比当前时间早,就把它拿出来重用。

# 开启 reuse
net.ipv4.tcp_tw_reuse = 1

# 前提:必须开启 timestamp
net.ipv4.tcp_timestamps = 1

我的经验:tcp_tw_reuse 是这三个参数里最安全的。我在生产环境用了很多年,没出过问题。它只对「发起连接的一端」有效——也就是 connect 那一方。对于服务器监听端口(bind + listen),它不起作用。

说白了,这个参数适合客户端或 Nginx 反向代理这种主动发起连接的场景。服务器端用不上。

4.3 net.ipv4.tcp_tw_recycle —— 这个参数我劝你慎用

tcp_tw_recycle 比 reuse 激进得多。它启用后,内核会更快地回收 TIME_WAIT 状态的 socket,不再等 60 秒。

但问题来了——它依赖 timestamp 机制,而且对 NAT 环境有严重副作用。

参数 作用范围 依赖条件 NAT 兼容性 推荐场景
tcp_tw_reuse 客户端(connect) tcp_timestamps=1 兼容 高并发客户端、反向代理
tcp_tw_recycle 服务端(accept) tcp_timestamps=1 不兼容 不推荐使用
tcp_fin_timeout 主动关闭方 无 兼容 短连接服务

为什么会不兼容 NAT?你想想看,NAT 后面多台机器共用同一个公网 IP,它们的 timestamp 可能不同步。内核收到一个 SYN 包,发现 timestamp 比之前记录的还小,就直接丢掉了。结果就是——部分用户死活连不上。

我曾经踩过的坑:有一次帮客户排查问题,用户反馈「手机能连,电脑连不上」。查了两天,最后发现是某台服务器开了 tcp_tw_recycle。NAT 出口的 timestamp 乱序,导致部分连接被静默丢弃。从那以后,我再也没在生产环境开过这个参数。

而且,从 Linux 4.12 开始,tcp_tw_recycle 已经被移除了。如果你用的是较新内核,这个参数已经不存在了。所以,忘掉它吧。

4.4 net.ipv4.tcp_fin_timeout —— 控制 FIN_WAIT_2 的等待时间

这个参数和 TIME_WAIT 没有直接关系,它控制的是 FIN_WAIT_2 状态的超时时间。

四次挥手中,主动关闭方发送 FIN 后进入 FIN_WAIT_1,收到 ACK 后进入 FIN_WAIT_2。正常情况下,对端也会发 FIN,然后本端进入 TIME_WAIT。但如果对端不发了呢?FIN_WAIT_2 就会一直挂着。

默认值是 60 秒。我个人习惯把它调小一点,比如 10 秒或 15 秒。

# 设置 FIN_WAIT_2 超时时间为 15 秒
net.ipv4.tcp_fin_timeout = 15

调小这个值的好处是:如果对端异常不发送 FIN,本端能更快释放资源。但别调得太小,比如设成 2 秒——万一网络延迟大,正常连接可能被误杀。

4.5 三个参数的配置建议

说了这么多,直接给结论吧:

  1. tcp_tw_reuse = 1 —— 建议开启,特别是 Nginx、HAProxy 这类反向代理场景
  2. tcp_tw_recycle = 0 —— 别开,新内核已经删了,旧内核也别碰
  3. tcp_fin_timeout = 15~30 —— 根据业务调整,短连接服务可以设小一点

另外,别忘了开启 timestamp:

net.ipv4.tcp_timestamps = 1

没有 timestamp,tcp_tw_reuse 是不工作的。

4.6 参数调优的完整配置示例

下面是我常用的配置模板,适用于高并发短连接场景:

# /etc/sysctl.d/99-port-reuse.conf

# 基础参数
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# 端口范围(根据并发量调整)
net.ipv4.ip_local_port_range = 1024 65535

# 其他辅助参数
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_syncookies = 1

配置完记得生效:

sysctl -p /etc/sysctl.d/99-port-reuse.conf

4.7 一张图看懂三个参数的关系

下面这张图展示了 TCP 连接关闭过程中,这三个参数分别在哪个阶段起作用:

TCP 四次挥手与内核参数作用点 主动关闭方(Client/Server) ESTABLISHED FIN_WAIT_1 FIN_WAIT_2 TIME_WAIT (60s) CLOSED 被动关闭方 ESTABLISHED CLOSE_WAIT LAST_ACK CLOSED tcp_fin_timeout tcp_tw_reuse / recycle FIN → ← ACK FIN → ← ACK

从图上可以清楚看到:tcp_fin_timeout 控制 FIN_WAIT_2 的等待时间,而 tcp_tw_reuse/recycle 作用于 TIME_WAIT 阶段。两者分工明确,互不干扰。

4.8 总结一下

这三个参数,说白了就是用来缓解 TIME_WAIT 问题的。但每个参数都有自己的脾气:

  • tcp_tw_reuse —— 安全,推荐开启,但只对主动连接方有效
  • tcp_tw_recycle —— 危险,别碰,新内核已经删了
  • tcp_fin_timeout —— 辅助参数,调小能加速资源释放

嗯,记住一句话:不要为了调优而调优。先搞清楚你的业务场景,再决定动哪个参数。不然,调优变调坑,那就尴尬了。