深入TCP协议——tcp_tw_reuse和tcp_tw_recycle

简介: 我们已经知道TCP四次挥手中,主动方在收到被动方的FIN数据包之后会进入TIME_WAIT状态等待2MSL的时间后才进入CLOSED。在 Linux 操作系统下,TIME_WAIT 状态的持续时间是 60 秒,这意味着这 60 秒内,客户端一直会占用着这个端口,这是有一定的开销的。如果如果主动关闭连接方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。

我们已经知道TCP四次挥手中,主动方在收到被动方的FIN数据包之后会进入TIME_WAIT状态等待2MSL的时间后才进入CLOSED。在 Linux 操作系统下,TIME_WAIT 状态的持续时间是 60 秒,这意味着这 60 秒内,客户端一直会占用着这个端口,这是有一定的开销的。如果如果主动关闭连接方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。


不过,Linux 操作系统提供了两个可以系统参数来快速回收处于 TIME_WAIT 状态的连接(这两个参数都是默认关闭的),分别是net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle。


一、tcp_tw_reuse

如果开启该选项的话,客户端 在调用 connect() 函数时,内核会随机找一个 TIME_WAIT 状态超过 1 秒的连接给新的连接复用。这其实就是相当于缩短了 TIME_WAIT 状态的持续时间。


这里既然我们缩短了TIME_WAIT状态的时间,使其不是2MSL,那么因为引入TIME_WAIT解决的问题就再次出现了,所以tcp_tw_reuse也需要解决这两个问题:


防止历史连接中的数据,被后面相同四元组的连接错误的接收

保证被动关闭连接的一方能够正确的关闭

首先看第一个问题,因为如果在历史连接上建立了新的连接,而网络中此时还有历史连接残留的数据包存活,那么它们有可能在新连接建立之后到达,那么就会导致接收到错误的数据。TIME_WAIT的解决方法就是等待2MSL之后才进入CLOSED,也就是说耗死了网络中残留的数据包,保证新连接建立时历史数据包全都死亡了。而前面我们提到了,使用tcp_tw_reuse只需要TIME_WAIT状态超过1秒就可以建立新的连接,所以它需要采用别的方式来解决这个历史数据报的问题。这里是引入了tcp_timestamps这个时间戳选项和防回绕序列号算法PAWS来解决。


防回绕序列号算法要求连接双方维护最近一次收到的数据包的时间戳(Recent TSval),每收到一个新数据包都会读取数据包中的时间戳值跟 Recent TSval 值做比较,如果发现收到的数据包中时间戳不是递增的,则表示该数据包是过期的,就会直接丢弃这个数据包。历史数据包显然时间戳必然是早于新连接建立后保存的时间戳的,所以就避免了接收旧连接残留数据的问题。


序列号本来也是递增的,正常来说延迟的数据包重新到达时序号是对不上的,因为这期间有其他新的数据包到达,会改变期待的序列号。而如果在延迟期间序列号兜了一圈又回到了原本的位置,那么延迟的数据包正好就能被正确的读取,这会导致数据错误,而时间戳保存了最近一次收到数据包的时间,所以就避免了这种情况。


即序列号会回绕,所以无法根据序列号来判断当前数据包是不是旧的数据包。


所以显然使用tcp_tw_reuse需要打开 TCP 时间戳选项,即 net.ipv4.tcp_timestamps=1(默认即为 1)。


但是其实tcp_tw_reuse还是存在风险:因为PAWS算法不会防止过期的RST,所以如果前面有残留的RST报文,在新连接建立之后到达,那么就会导致新连接被这个历史的RST包中断。如果此时不跳过 TIME_WAIT 状态,而是停留 2MSL 时长,那么这个 RST 报文就不会出现下一个新的连接。


至于第二个问题,tcp_tw_reuse并没办法解决,如果第四次挥手的 ACK 报文丢失了,而此时这个端口已经被另一个连接使用了,那么原服务端重发的FIN就会发送失败,从而接收到RST报文而异常关闭。


所以说tcp_tw_reuse还是存在风险的。


二、tcp_tw_recycle

这种机制也依赖时间戳选项,开启tcp_tw_recycel后服务器将会缓存每个套接字的最新时间戳,TIME-WAIT状态将会在超时重发(RTO)间隔后移除。不同的是tcp_tw_recycle使用的是per-host的PAWS机制,它是队对端IP做PAWS检查(tcp_tw_reuse是对四元组做PAWS检查),即Linux将会放弃所有来自远程端的timestamp时间戳小于上次记录的时间戳(也是远程端发来的)的任何数据包,除非TIME-WAIT状态已经过期**。**这个时候如果客户端网络环境是用了 NAT 网关,那么客户端环境的每一台机器通过 NAT 网关后,都会是相同的 IP 地址,在服务端看来,就好像只是在跟一个客户端打交道一样,无法区分出来(因为它是以对端IP为粒度的,而不是IP+端口)。当客户端NAT环境有多个设备,每个设备的时间肯定不会完全一致,所以访问服务端的时候,服务端观察到这个IP携带的timestamp不是单调递增的,所以会导致大量的包丢失(假如A、B机器都位于同个NAT网关下,A机器的时间要比B机器时间慢,那么当B机器发送了SYN数据包建立连接之后,A机器此时发送的SYN包就会被丢弃)。


实际上大多数情况下,都是tcp连接上的客户端主动发起fin关闭,即time_wait状态会出现在客户端,而服务端不会出现time_wait状态,这种情况服务端是不需要考虑优化回收time_wait的。但是现代的大多数应用部署在服务器上,它即会作为服务端对外提供服务,也会作为客户端去请求其他服务器上的接口,前者因为是作为服务端,很少出现time_wait状态,后者作为客户端很容易在服务器上产生大量TCP time_wait状态,我们想快速回收time_wait,主要是针对后者这种情况。此时如果我们想解决后者time_wait过多的情况,在服务器(注意是服务器,不是服务端)上开启tcp_tw_recycle快速回收端口,因为这台服务器上的应用同时又是作为服务端对外提供服务,tcp_tw_recycle触发的pre-host PAWS机制,会使得客户端超时异常。


所以net.ipv4.tcp_tw_recycle=1不能轻易开启,如果非要开启只能在以下两种场景:


并发内部调用,一般不存在客户端proxy(即客户端NAT)

自己对上下游应用有较强的可控性,可以随时调整上下游的参数一起去适配

这台服务器上的应用,只会作为客户端频繁请求其他IP(一般情况都是客户端主动关闭TCP,产生time_wait状态),没有作为服务端对外提供服务。

参考文章:


被微信问懵了:既然开启 net.ipv4.tcp_tw_reuse 可以复用连接,为什么内核不默认开启呢? - 知乎 (zhihu.com)

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
机器学习/深度学习 网络协议 Java
聊聊 wireshark 的重传包和重复包(Duplicate Packets or TCP Retransmissions?)
聊聊 wireshark 的重传包和重复包(Duplicate Packets or TCP Retransmissions?)
|
8月前
|
网络协议 安全 应用服务中间件
|
关系型数据库 MySQL 数据库
图解MySQL【日志】——两阶段提交
两阶段提交是为了解决Redo Log和Binlog日志在事务提交时可能出现的半成功状态,确保两者的一致性。它分为准备阶段和提交阶段,通过协调者和参与者协作完成。准备阶段中,协调者向所有参与者发送准备请求,参与者执行事务并回复是否同意提交;提交阶段中,若所有参与者同意,则协调者发送提交请求,否则发送回滚请求。MySQL通过这种方式保证了分布式事务的一致性,并引入组提交机制减少磁盘I/O次数,提升性能。
1850 5
图解MySQL【日志】——两阶段提交
|
运维 网络协议 Linux
[linux]常见内核TCP参数描述与配置
[linux]常见内核TCP参数描述与配置
1390 0
|
Kubernetes 应用服务中间件 nginx
搭建Kubernetes v1.31.1服务器集群,采用Calico网络技术
在阿里云服务器上部署k8s集群,一、3台k8s服务器,1个Master节点,2个工作节点,采用Calico网络技术。二、部署nginx服务到k8s集群,并验证nginx服务运行状态。
5225 3
|
Kubernetes Linux 容器
如何在centos中关闭swap分区
在CentOS中,关闭swap分区可以通过临时关闭和永久禁用两种方式实现。临时关闭swap分区适用于临时测试和故障排除,而永久禁用swap分区则需要修改 `/etc/fstab`文件。通过遵循上述步骤,可以确保系统在没有swap的情况下稳定运行。这对于某些应用场景(如Kubernetes集群)是必要的配置步骤。
2163 3
|
Linux 网络安全 iOS开发
/config --prefix=/usr/local/ssl --shared 命令里的参数的作用
【4月更文挑战第18天】/config --prefix=/usr/local/ssl --shared 命令里的参数的作用
1023 3
|
网络协议 网络架构
MTU 和 MSS 关系、 IP分片、TCP分段
这篇文章详细解释了MTU(最大传输单元)和MSS(最大报文段长度)的关系,包括它们在不同网络层的作用、如何影响IP分片和TCP分段,以及实际网络环境中如何通过调整MTU和MSS来解决数据传输问题。
2874 0
|
缓存 应用服务中间件 nginx
Nginx 作为反向代理优化要点proxy_buffering
当nginx用于反向代理时,每个客户端将使用两个连接:一个用于响应客户端的请求,另一个用于到后端的访问; 那么,可以从如下配置起步: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # One worker per CPU-core.
10762 144