关于TIME_WAIT问题简述与优化
我不打算长篇大论,这篇文章将简明扼要的告诉大家TIME_WAIT引发了什么问题,如何优化。
为什么TIME_WAIT
主动关闭连接的一方最终进入TIME_WAIT状态等待一段才真正的释放内核中的连接记录,在释放记录之前这个连接使用的本地端口将一直被占用。
保持一段时间的TIME_WAIT的理由是:担心”ack N+1″没有送达,导致被动方重传”FIN N”,那么主动方应当再次响应”ack N+1″。
如果没有TIME_WAIT就直接复用该连接占用的端口,那么万一被动方重传”FIN N”,那么使用相同端口的新连接就会被错误关闭。
优化TIME_WAIT
谁主动关闭socket,谁TIME_WAIT。
如果双方响应正常,TIME_WAIT应该只是瞬间状态。
因此,不明确”客户端主动关闭”/”服务端主动关闭”就讨论TIME_WAIT就是耍流氓。
服务端主动关闭
无论有多少连接,服务端都只有一个端口,那就是监听端口,大量连接之间的差异仅仅是TCP 4元祖的客户端ip和port不同而已。
因此服务端TIME_WAIT压根不会耗尽端口,因为它就一个端口。
那么服务端就不需要优化了吗?对,没必要优化,一个TIME_WAIT的4元祖当遇到新的SYN时会复用,不需要特殊配置。
另外,当TIME_WAIT数量超过内核选项net.ipv4.tcp_max_tw_buckets的限制时,多余的TIME_WAIT连接将被立即关闭,然后在netstat -s中留下如下的溢出统计指标:
TCPTimeWaitOverflow: 127688100
客户端主动关闭
客户端每个连接都会随机选择一个本地端口,所以最终会导致客户端大量端口处于TIME_WAIT状态,这和服务端主动关闭是最大的不同。
所以我们通常所说的TIME_WAIT问题都是针对客户端的,只是好像很少有人提及这一点。
网上有一种优化手段是把net.ipv4.tcp_max_tw_buckets调低,这样TIME_WAIT连接就会被删除,但是这不是一个最佳手段哈。
目前唯一安全的选项就是同时开启如下2个选项:
- net.ipv4.tcp_timestamps=1(连接发起方和接收方都需要开启)
- net.ipv4.tcp_tw_reuse=1(只影响连接发起方)
上述配置只影响连接发起方,也就是客户端,对服务端是无效的。
它的作用是向外发起连接的时候,可以复用TIME_WAIT的端口,但是有一个前提:
该端口最后一次通讯时间距离当前系统时间>1秒
tcp timestamp选项的作用就是内核会记录端口的最后通讯时间,这样reuse选项才有判断依据。
那么客户端复用TIME_WAIT连接,对服务端有啥影响呢?
TIME_WAIT就是为了确保给被动方的FIN回复ACK成功,这样对方才会离开LAST-ACK彻底结束连接。
图中的叉号就是回复ACK丢失的情况,按道理应该继续TIME_WAIT等待被动关闭方重传FIN,然而我们这时候我们重用端口新建到服务端的连接,发送SYN会被对方忽略,因为对方还处在上一个连接的LAST-ACK状态。
被动方稍后重传FIN,客户端的端口已经是SYN-SENT新建连接状态,因此回复对方RST让对方退出LAST-ACK状态。此后,客户端重传SYN,被动关闭方正常回复SYN+ACK,连接建立完成。
看起来,唯一顾虑的就是SYN重传会不会导致连接变慢?可能需要实际测试测试。
另外还有2个辅助优化手段,就是扩大内核选择端口的范围,比如调大为:
net.ipv4.ip_local_port_range = 5120 65000
还有降低TIME_WAIT的保持时间(可以更短,比如10秒):
net.ipv4.tcp_fin_timeout=30
这样客户端可以有更多的端口使用。
总结
在所有机器上,生效如下参数:
- net.ipv4.tcp_timestamps=1
- net.ipv4.tcp_tw_reuse=1
- net.ipv4.ip_local_port_range = 5120 65000
- net.ipv4.tcp_fin_timeout=30
如果你的内核版本低,那么需要确保禁用掉一个垃圾选项,它已经在4.x内核里彻底删除了:
- net.ipv4.tcp_tw_recycle=0
这可以应对TIME_WAIT问题,但是不是解决短连接问题的所有参数(可以阅读我的上一篇关于K8S的博客)。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~

🐂🍺
如果双方响应正常,TIME_WAIT应该只是瞬间状态。这个没有看懂
1
1
1
1
1