阅读本文约需要10分钟,您可以先关注我们,避免下次无法找到。
01 承上启下
上篇介绍了由tcp_tw_recycle引发的业务超时问题的临时解决方案,以及由此引发成哥的深入思考。
既然本次问题和网络设备的NAT、以及Linux内核TCP参数都有关系,成哥就要从这两方面进行深入挖掘。
上篇详细挖掘了私有云中两处涉及NAT的节点业务流走向。本篇将继续挖掘,深挖TCP参数,彻底刨出问题根源。
02 深挖TCP参数
要想理解TCP参数的深层次含义,就要以timestamps、tcp_tw_recycle等关键词为线索,硬啃Linux用户手册、RFC文档以及Linux内核仓库(Kernel.org git repositories)。
研究文档资料的过程是个艰难的过程,比如需要彻底理解timestamps,就要弄清什么是RTO,什么是RTT、什么是RTTM等等。比如要理解单调递增时间戳,就要理解什么是Per-host PAWS,然后PAWS概念中还有其他一堆概念。
啃文档的过程是反向理解,逐步深入的。这里成哥给大家做个正向展示,方便大家理解。
(1)Timestamp
Timestamp(时间戳)是TCP协议首部中的可选项。
10字节的timestamp选项中,TSval是发送方填写的发送时间。TSecr是发送方收到对方消息包的发送时间,即对方消息包中的TSval,由于这个是SYN包,所以此时并没有收到任何对方的消息,因此TSecr为0。
Timestamp的执行步骤如下:
A.发送方和接收方同时启用TCP协议的timestamp选项。
B.发送方发送数据时,将timestamp(表示此刻时间)放在TCP选项中,作为TCP头部内容发送出去。
C.接收方收到数据包后,在对应的ACK包的TCP选项中将收到的timestamp返回给发送方。
D.发送方收到ACK包后,用当前时刻的时间减去ACK包中的timestamp就能得到准确的RTT (Round Trip Time,即数据包的往返时间)
Timestamp两个主要作用:
A.测量TCP会话两端的通信延迟,即RTTM(Round Trip Time Measurement,往返时间测量)
有了RTTM机制,才能够设置TCP超时重传时间,即RTO(Retransmission Time Out)。 因为只有确认数据包的往返时间,才能给定一个TCP的超时重传时间。需要知道RTT会相应地随网络环境发生改变,TCP需要跟踪这些变化并动态调整RTO。
同时,有了timestamp测量通信延迟的机制,就可以取代tcp_skb_cb->when(socket buffer,Linux网络代码中最根本的数据结构,收发数据包都是通过skb)的测量缺陷。
B.处理序列号(Sequence)缠绕问题 ,即PAWS(Protect Against Wrapped Sequences)
TCP接收方在收到一个数据包后,会比较最近两次数据包中的timestamps,如果本次timestamp比较新,则直接判断本次数据包是新的报文。
PAWS机制非常重要,我们在下文中展开讨论。
(2)Per-host PAWS
PAWS字面意思就是防止序列号缠绕,首先我们要了解序列号为什么会缠绕。
TCP首部的序列号(sequence)是一个32bit的字段, 能够使用的数量是2的32次方。序列号字段在TCP首部中的位置如下:
当在高带宽、大流量的业务模型下,TCP序列号可能会在较短的时间就被重复使用,也就是说出现缠绕,就可能导致同一条TCP流在短时间内出现序号一样的两个合法的数据包及其确认包。
PAWS机制就是解决序列号缠绕问题设计出来的。
从RFC1323中注意到PAWS是基于Per-host的,即PAWS要求所有来自于同一个IP地址的TCP数据包的timestamp应该是单调递增的。当收到的数据包中的timestamp值小于之前接收到数据包的timestamp值后,会认为这是一个过期的数据包,需要将其丢弃。
同时,如果在针对per-host的使用PAWS中的机制,则会解决TIME-WAIT中考虑的上一个TCP流的数据包在下一个TCP流中被当做有效数据包的情况,这样就没有必要等待2*MSL来结束TIME-WAIT了,这就TIME_WAIT快速回收。
英文原版描述如下:
也就是说启用了Per-host PAWS机制,就开启了TIME_WAIT快速回收。接下来要研究下TIME_WAIT快速回收,以及如何启用Per-host PAWS机制。
(3)TIME_WAIT
我们来看看TIME_WAIT的概念。TIME_WAIT是TCP状态迁移过程中的一个状态,即主动发起TCP会话关闭一方在释放TCP连接前最后的等待时间。此状态如下所示:
TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL。它是任何报文段被丢弃前在网络内的最长时间。Linux系统默认的2MSL是60秒,也就是TCP流在完成数据传输后,需要等待60秒才能真正关闭。
再来说说TIME_WAIT快速回收,在启用Per-host PAWS机制后,就开启了TIME_WAIT快速回收,Linux可以抛弃60秒TIME-WAIT时间,直接缩短到3.5倍RTO时间。因为PAWS机制能够的防止了序列号缠绕,有效地保证了之前报文缠绕到新的TCP连接中。
(4)tcp_tw_recycle
tcp_tw_recycle参数的字面意思就是tcp time_wait recycle,该参数就是用于Linux内核快速回收TIME_WAIT状态。
从Linux用户手册中看到tcp_tw_recycle开启就可以"Enable fast recycyling of TIME_WAIT socket"即开启TIME_WAIT快速回收,也就是启用了Per-host PAWS机制。同时tcp_timestamps也需要开启(默认开启)。
03 NAT和TCP参数结合分析
前文分析私有云中涉及的NAT两个节点,为使Web服务器的应答流量能够经过负载均衡器,负载均衡器使用了SNAT功能,将用户的源地址转换成负载均衡器上的Self IP地址。从即Web服务器上看到的用户请求都是同一个IP地址。
这就有个问题,就是用户请求发出后,都会在各自的TCP报文中打上timestamp,不同用户的timestamp都是各自的本地时间,这就无法保证统一。不同用户请求经过私有云进入负载均衡器时,负载均衡器又通过SNAT将不同用户的源地址都映射为其Self IP地址。
用户流量进入Web服务器后,正巧Web服务器为对TIME_WAIT进行快速回收,开启了tcp_timestamps和tcp_tw_recycle两个内核参数。这也就开启了Per-host PAWS机制。
通过Per-host PAWS进制的校验,发现来自于同一host (即负载均衡器的Self-IP地址)存在timestamp不按序单调递增的的情况,然后就对"问题"数据包进行了丢弃。
从业务层面看,就出现了文章开头说的业务超时现象。
04 解决方案
常见的解决方法有两种,
(1)第一种就是我们之前介绍的,通过关闭Linux内核tcp_tw_recycle参数来解决用户请求超时。
修改后的参数设置如下:
$ vi /etc/sysctl.conf
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_timestamps = 1
$ sysctl -p
但是这种方法虽然解决了用户请求超时问题,开始也关闭了TIME_WAIT快速回收。这就相当于杀敌一千自损八百。
(2)第二种方法是调整网络设备的参数。
刚才说过负载均衡器启用SNAT也是造成此问题的原因之一。那就关闭SNAT。
但是我们前文说,如果负载均衡器不启用SNAT,流量回程就会有问题。
让流程回程重新经过负载均衡器,也有解决办法。可在核心交换机上配置PBR,通过ACL抓取Web服务器的IP流量,将回程流量的下一跳强行指向负载均衡器。
这种方法比第一种方法好,Web服务器的TIME_WAIT快速回收仍然可用。
05 总结
本文是成哥基于一次业务超时问题而进行思考的过程,虽然查阅资料的过程比较艰难,需要分析和研究的技术点比较多。但是成哥乐在其中,搞清楚问题根本原因后,还是比较有成就感的。希望大家也能从成哥的文章中学习到有用的知识,也能乐在其中。
--END--
@IT管理局专注计算机领域技术、大学生活、学习方法、求职招聘、职业规划、职场感悟等类型的原创内容。期待与你相遇,和你一同成长。
相关文章推荐:
本文暂时没有评论,来添加一个吧(●'◡'●)