tcp TIME_WAIT
进入主题前必须做铺垫啊,讲讲TIME_WAIT
.因为TCP连接是双向的,所以在关闭连接的时候,两个方向各自都需要关闭。先发FIN
包的一方执行的是主动关闭;后发FIN
包的一方执行的是被动关闭。主动关闭的一方会进入TIME_WAIT
状态,并且在此状态停留两倍的MSL
(最大报文存活时间,一般Linux内核设置30秒)时长。
为什么主动方要傻乎乎等2MSL呢?不等,行不行?
TCP目的是可靠传输,主动关闭的一方发出FIN
,被动方回复ACK
,接着被动方发送FIN
,主动方收到被动关闭的一方发出的FIN
包后,回应ACK
包,同时进入TIME_WAIT
状态,但是因为网络原因,主动关闭的一方发送的这个ACK包很可能延迟,从而触发被动连接一方重传FIN包。极端情况下,这一去(ACK
去被动方)一回(重传FIN
回来),就是两倍的MSL
时长。
如果主动关闭的一方跳过TIME_WAIT
直接进入CLOSED
,或者在TIME_WAIT
停留的时长不足两倍的MSL
,那么当被动关闭的一方早先发出的FIN
延迟包到达或者重传FIN
包到达后,就可能出现类似下面的问题:
- 主动方旧的TCP连接已经不存在了,主动方只能返回
RST
包 - 主动方新的TCP连接被建立起来了,延迟包可能干扰新的连接
所以, TIME_WAIT
必须等,2MSL不能少
减少TIME_WAIT
TIME_WAIT
期间,资源不会释放,现在都追求高性能高并发,快速释放资源是躲不掉的.对于客户端因为有端口65535问题,TIME_WAIT
过多直接影响处理能力. 对于服务器,无端口数量限制的问题,Linux优化也很给力,每个处于TIME_WAIT
状态下连接内存消耗很少, 而且也能通过tcp_max_tw_buckets
= ${你要的阈值} 配置最大上限,但是对于短连接为主的web服务器,几十万的连接,基数很大,耗得内存也不小哦.快速释放总是好的
-
tcp_tw_recycle
:回收TIME_WAIT
连接
对客户端和服务器同时起作用,开启后在 3.5*RTO 内回收,RTO 200ms~ 120s 具体时间视网络状况。RTO
(Retransmission TimeOut)重传超时时间.内网状况比tcp_tw_reuse
稍快,公网尤其移动网络大多要比tcp_tw_reuse
慢,优点就是能够回收服务端的TIME_WAIT
数量但是,有个小坑:当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。客户端处于NAT很常见,基本公司家庭网络都走NAT.
-
tcp_tw_reuse
:复用TIME_WAIT连接 只对客户端起作用,1秒后
才能复用,当创建新连接的时候,如果可能的话会考虑复用相应的TIME_WAIT
连接。通常认为tcp_tw_reuse
比tcp_tw_recycle
安全一些,这是因为一来TIME_WAIT
创建时间必须超过一秒
才可能会被复用;二来只有连接的时间戳是递增的时候才会被复用。
客户端请求服务器,服务器响应后主动关闭连接,TIME_WAIT
存在于服务器,服务器是被连接者,没有复用一说,所以只对客户端起作用.如果是客户端主动关闭,TIME_WAIT
存在于客户端,这个时候再次连接服务器,可以复用之前TIME_WAIT
留下的半废品.
-
tcp_timestamps
:以上两点,必须在客户端和服务端timestamps 开启时才管用(默认开启) 需要根据timestamp的递增性来区分是否新连接
总结
- 客户端
tcp_tw_reuse
复用连接管用,tcp_tw_recycle
有用,但是客户端主要不是接受连接,用处不大 - 服务器
tcp_tw_recycle
回收连接管用,tcp_tw_reuse
复用无效.为了减少TIME_WAIT
留在服务器,可以在服务器开启KeepAlive
,尽量不让服务器主动关闭,而是客户端主动关闭,减少TIME_WAIT
产生.
More
-
tcp_tw_reuse
和SO_REUSEADDR
说的完全不是一个东西.
打个例子,举个比方: 一般来讲,在我们写一个server的时候,会用到SO_REUSEADDR
,因为,当server正在工作的时候,需要重启,这时候,可能server的一些子进程还在工作,也或者一些连接还处于time_wait
状态,而且time_wait
状态的时间一般比较长,如果没有SO_REUSEADDR
,则会因为端口被占用而bind失败,无法启动server.
这种模式下典型例子Nginx
,一个master进程监听一个端口,接受连接,将逻辑工作分发给子进程.显然,单进程监听分发存在瓶颈,能不能多个进程监听同一个端口呢?
-
SO_REUSEPORT
Linux kernel 3.9
带来了SO_REUSEPORT
特性,就是解决上面的问题,让多个进程同时监听同一个端口,干掉单点监听瓶颈,允许端口重复捆绑