TIME_WAIT过多的危害以及解决TIME_AWAIT过多方案

时间:2025-02-21 18:22:25

TIME_WAIT过多危害

  • 网络情况不好时,如果主动方无TIME_WAIT等待,关闭前个连接后,主动方与被动方又建立起新的TCP连接,这时被动方重传或延时过来的FIN包过来后会直接影响新的TCP连接;
  • 同样网络情况不好并且无TIME_WAIT等待,关闭连接后无新连接,当接收到被动方重传或延迟的FIN包后,会给被动方回一个RST包,可能会影响被动方其它的服务连接。
  • 过多的话会占用内存,一个TIME_WAIT占用4k大小

解决方法

相关参数优化调整(当然得根据服务器的实际情况配置,这里着重讲参数意义):
既然知道了TIME_WAIT的用意了,尽量按照TCP的协议规定来调整,对于tw的reuse、recycle其实是违反TCP协议规定的,服务器资源允许、负载不大的条件下,尽量不要打开,当出现TCP: time wait bucket table overflow,尽量调大下面参数:
tcp_max_tw_buckets = 256000
调整次参数的同时,要调整TIME_WAIT_2到TIME_WAIT的超时时间,默认是60s,优化到30s:
net.ipv4.tcp_fin_timeout = 30
其它TCP本身的配合参数类似与synack重传次数、syn重传次数等以后介绍,优化后也是有所益处的。

 下面再说一下linux里TIME_WAIT专有的优化参数reuse、recycle,默认也都是关闭的,这两个参数必须在timestamps打开的前提下才能生效使用

net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_reuse = 1
机器作为客户端时起作用,开启后time_wait在一秒内回收
net.ipv4.tcp_tw_recycle = 0 (不要开启,现在互联网NAT结构很多,可能直接无法三次握手)

开启后在3.5*RTO(RTO时间是根据RTT时间计算而来)内回收TIME_WAIT,并60s内同一源ip主机的socket
connect请求中的timestamp必须是递增的,对于服务端,同一个源ip可能会是NAT后很多机器,这些机器timestamp递增性无可保证,服务器会拒绝非递增请求连接,直接导致不能三次握手。

注意

tcp_tw_recycle:顾名思义就是回收TIME_WAIT连接。可以说这个内核参数已经变成了大众处理TIME_WAIT的万金油,如果你在网络上搜索TIME_WAIT的解决方案,十有八九会推荐设置它,不过这里隐藏着一个不易察觉的陷阱:

当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。参考:tcp_tw_recycle和tcp_timestamps导致connect失败问题。

tcp_tw_reuse:顾名思义就是复用TIME_WAIT连接。当创建新连接的时候,如果可能的话会考虑复用相应的TIME_WAIT连接。通常认为「tcp_tw_reuse」比「tcp_tw_recycle」安全一些,这是因为一来TIME_WAIT创建时间必须超过一秒才可能会被复用;二来只有连接的时间戳是递增的时候才会被复用。官方文档里是这样说的:如果从协议视角看它是安全的,那么就可以使用。这简直就是外交辞令啊!按我的看法,如果网络比较稳定,比如都是内网连接,那么就可以尝试使用。

不过需要注意的是在哪里使用,既然我们要复用连接,那么当然应该在连接的发起方使用,而不能在被连接方使用。举例来说:客户端向服务端发起HTTP请求,服务端响应后主动关闭连接,于是TIME_WAIT便留在了服务端,此类情况使用「tcp_tw_reuse」是无效的,因为服务端是被连接方,所以不存在复用连接一说。让我们延伸一点来看,比如说服务端是PHP,它查询另一个MySQL服务端,然后主动断开连接,于是TIME_WAIT就落在了PHP一侧,此类情况下使用「tcp_tw_reuse」是有效的,因为此时PHP相对于MySQL而言是客户端,它是连接的发起方,所以可以复用连接。

说明:如果使用tcp_tw_reuse,请激活tcp_timestamps,否则无效。

参考:/2013/12/31/316