Linux服务器为什么会积累大量TIME_WAIT连接,端口都不够用了
服务器跑着跑着突然开始报"无可用端口"、连接建立不上,netstat -an | grep TIME_WAIT | wc -l一查,几万个连接卡在TIME_WAIT状态动弹不得。这种情况在跑代理面板、反向代理这类需要频繁建立短连接的服务上特别常见,很多人第一反应是去搜"TIME_WAIT优化",抄一段网上流传很广的内核参数配置,结果有的抄对了,有的抄了个更大的坑。
TIME_WAIT不是浪费,是保护机制
TCP连接主动关闭的那一方(通常是发起连接的客户端,也可能是主动断开的服务端,比如反向代理去连后端),断开之后不会立刻释放这个端口,而是停留在TIME_WAIT状态一段时间(标准时长是2倍的MSL,Linux下这个总时长通常在1分钟左右)。这段时间不是白等的,它在防两件事:一是防止这个连接里还在网络上漂着的延迟旧数据包,被误认成属于后面复用了同一个四元组(本地IP、本地端口、远端IP、远端端口)的新连接;二是保证四次挥手最后那个ACK包如果丢了,对方重传FIN的时候,这边还能正常响应,不会导致连接没法正常关闭。
这套机制存在了几十年,是TCP协议本身故意这么设计的,不是Linux的失误。
什么情况下会真的端口不够用
TIME_WAIT状态的连接占用的端口不能被同样四元组的新连接复用,如果短时间内建立、断开连接的频率特别高(典型场景就是代理服务处理大量客户端的短连接请求),TIME_WAIT堆积的速度超过它自然过期释放的速度,可用端口池就会被迅速耗尽——本地端口范围默认大概是2.8万个左右,tcp_max_tw_buckets这个参数(默认1.8万左右)同时也在限制系统能同时保留的TIME_WAIT连接总数,两者取较小值,就是这台机器实际能扛住的上限。
网上流传的"修复方法",一半好用一半是坑
搜TIME_WAIT优化,十有八九会同时搜到两个参数打包出现:tcp_tw_reuse和tcp_tw_recycle,很多老教程让你把两个一起打开,这个建议只对了一半。
tcp_tw_reuse相对安全,打开之后能让本机在满足条件(主要靠TCP时间戳判断新旧连接不会冲突)的情况下,快速复用还处于TIME_WAIT状态的端口去发起新连接,主要在你的服务器作为客户端主动连出去的场景(比如反向代理连后端)时有效:
1 | sysctl -w net.ipv4.tcp_tw_reuse=1 |
tcp_tw_recycle就是那个坑了——这个参数在Linux 4.12版本已经被彻底移除,但很多老教程写的时候它还在,抄这些老教程在新内核上会直接报错设置不了;更要命的是,就算是在还保留这个参数的老系统上,打开它对处于NAT网络后面的客户端极度不友好——同一个NAT出口下的不同用户,因为时间戳判断逻辑的缺陷,会出现连接被错误拒绝、访问不了服务的情况,公网服务打开这个参数,等于把一部分NAT后面的正常用户直接拒之门外。这不是危言耸听,这个坑连大厂都踩过,后来的内核版本干脆把这个参数直接删掉了断绝后患。
线上服务不建议打开tcp_tw_recycle,如果是老教程抄来的配置文件里还留着这一行,建议删掉。
更稳妥的应对方式
除了tcp_tw_reuse,把tcp_max_tw_buckets适当调大也是常见做法,给系统更大的TIME_WAIT缓冲空间:
1 | sysctl -w net.ipv4.tcp_max_tw_buckets=55000 |
如果这台VPS上跑的是多协议代理一键脚本这类需要处理大量并发连接的服务,这几个参数值得留意一下,别等到端口耗尽报错了才想起来查。跟之前为什么开了BBR网速却感觉一点没提升一样,这类内核网络参数经常被打包在同一份"VPS优化脚本"里一股脑塞给你,弄清楚每个参数具体在做什么、哪些能抄哪些不能抄,比照单全收更重要。