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 三个参数的配置建议
说了这么多,直接给结论吧:
- tcp_tw_reuse = 1 —— 建议开启,特别是 Nginx、HAProxy 这类反向代理场景
- tcp_tw_recycle = 0 —— 别开,新内核已经删了,旧内核也别碰
- 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_fin_timeout 控制 FIN_WAIT_2 的等待时间,而 tcp_tw_reuse/recycle 作用于 TIME_WAIT 阶段。两者分工明确,互不干扰。
4.8 总结一下
这三个参数,说白了就是用来缓解 TIME_WAIT 问题的。但每个参数都有自己的脾气:
- tcp_tw_reuse —— 安全,推荐开启,但只对主动连接方有效
- tcp_tw_recycle —— 危险,别碰,新内核已经删了
- tcp_fin_timeout —— 辅助参数,调小能加速资源释放
嗯,记住一句话:不要为了调优而调优。先搞清楚你的业务场景,再决定动哪个参数。不然,调优变调坑,那就尴尬了。