time-wait快速回收与复用

time-wait

Posted by rosewind on April 7, 2019

前言

之前留意过为什么4次挥手最后有time-wait的阶段。如果在高并发的情况下,有大量的短连接,使得机器上有大量处在TIME_WAIT阶段的连接,无法创建新的socket连接,这时如何解决呢?

4次挥手的过程

解决方案

a、修改TIME_WAIT连接状态的上限值
b、启动快速回收机制
c、开启复用机制
d、修改短连接为长连接方式

快速回收

什么是快速回收机制?既然前面说了TIME_WAIT等待2*MSL时长是有必要的,怎么又可以快速回收了? 快速回收机制是系统对tcp连接通过一种方式进行快速回收的机制,对应内核参数中的net.ipv4.tcp_tw_recycle,要搞清楚这个参数,就不得不提一下另一个内核参数:net.ipv4.tcp_timestamps

net.ipv4.tcp_timestamps是在RFC 1323中定义的一个TCP选项。
tcp_timestamps的本质是记录数据包的发送时间
TCP作为可靠的传输协议,一个重要的机制就是超时重传。因此如何计算一个准确(合适)的RTO对于TCP性能有着重要的影响。而tcp_timestamp选项正是*主要*为此而设计的。 

当timestamp和tw_recycle两个选项同时开启的情况下,开启per-host的PAWS机制。从而能快速回收处于TIME-WAIT状态的TCP流。

PAWS — Protect Againest Wrapped Sequence numbers 目的是解决在高带宽下,TCP序号可能被重复使用而带来的问题。 PAWS同样依赖于timestamp,并且假设在一个TCP流中,按序收到的所有TCP包的timestamp值都是线性递增的。而在正常情况下,每条TCP流按序发送的数据包所带的timestamp值也确实是线性增加的。 如果针对per-host的使用PAWS中的机制,则会解决TIME-WAIT中考虑的上一个流的数据包在下一条流中被当做有效数据包的情况,这样就没有必要等待2*MSL来结束TIME-WAIT了。只要等待足够的RTO,解决好需要重传最后一个ACK的情况就可以了。因此Linux就实现了这样一种机制: 当timestamp和tw_recycle两个选项同时开启的情况下,开启per-host的PAWS机制。从而能快速回收处于TIME-WAIT状态的TCP流。

重用机制

net.ipv4.tcp_tw_reuse = 1

如果能保证以下任意一点,一个TW状态的四元组(即一个socket连接)可以重新被新到来的SYN连接使用:

  1. 初始序列号比TW老连接的末序列号大
  2. 如果使能了时间戳,那么新到来的连接的时间戳比老连接的时间戳大

如何理解这2个条件?

既然TIME_WAIT不是为了阻止新连接,那么只要能证明自己确实属于新连接而不是老连接的残留数据,那么该连接即使匹配到了TIME_WAIT的四元组也是可以接受的,即可以重用TIME_WAIT的连接。如何来保证呢?很简单,只需要把老的数据丢在窗口外即可。为此,只要新连接的初始序列号比老连接的FIN包末尾序列号大,那么老连接的所有数据即使迟到也会落在窗口之外,从而不会破坏新建连接! 即使不使用序列号,还是可以使用时间戳,因为TCP/IP规范规定IP地址要是唯一的,根据这个唯一性,欲重用TIME_WAIT连接的新连接的必然发自同一台机器,而机器时间是单调递增不能倒流的,因此只要新连接带有时间戳且其值比老连接FIN时的时间戳大,就认为该新连接是可以接受的,时间戳重用TW连接的机制的前提是IP地址唯一性导出的发起自同一台机器,那么不满足该前提的则不能基于此来重用TIME_WAIT连接,因此NAT环境不能这么做遍成了自然而然的结论。

参考

关于TIME_WAIT重用与RFC1337

一次即时通讯项目TIME_WAIT过多引发的记录

time_wait的快速回收和重用