TCP系列21—重传—11、TLP
一、介绍
Tail Loss Probe (TLP)是同样是一个发送端算法,主要目的是使用快速重传取代RTO超时重传来处理尾包丢失场景。在一些WEB业务中,如果TCP尾包丢失,如果依靠RTO超时进行重传会带来比较大的延迟,进而影响用户体验。如果一个TCP连接没有在一段时间内没有收到ACK报文,TLP会强制传输还没有收到ACK确认的报文里面的最后一个报文或者未发送的新报文(传输的这个报文就叫做loss probe)。这里强制传输是指loss probe的发送不受到拥塞控制的限制但是同样收到对方通告的接收窗口的限制。这个loss probe报文如果顺利到达对端就会有效的触发一个基于SACK的快速重传,实际上TLP结合增强ER和FACK功能可以完全使用快速重传取代尾包的第一次RTO超时,从而有效的提升了TCP性能。TLP只能使用在支持SACK的TCP连接上并且需要连接处于Open状态(Open状态是linux拥塞控制中的一种状态,表示一个发送端顺序接收到了没有SACK选项的ACK报文同时没有发现任何丢包的痕迹,例如发现了RTO超时的丢包痕迹就不会触发TLP)。
RTO超时重传除了会带来比较大的延迟外,还会对拥塞控制产生严重的影响,产生RTO超时重传的情况有很多种,比如:
尾包连续丢失,比如传输了4个报文,最后的三个报文都发生了丢失,那么只能依靠RTO超时来进行重传
整个发送窗口的报文丢失,比如发送窗口只允许发送2个tcp报文,tcp需要发送4个报文的时候,如果前2个报文发生丢失,那么也只是依靠RTO超时来触发重传
没有足够的dup ACK来触发快速重传,ER和thin stream对于这种场景有一些改善
链路时延突然增大,这种也会导致RTO超时,产生无效重传。
TLP的主要目的就是解决前两种场景,即尾包连续丢失和整个发送窗口的报文都丢失。在这两种情况下,如果一个TCP连接在一段时间里面没有收到ACK确认包,又不能发出新数据,则大概率会发生RTO超时重传,这时候就可以通过发送loss probe来触发SACK反馈进行快速重传,等待的这段超时时间则称呼为probe timeout (PTO)。PTO应该是限制在小于等于RTO的。
二、TLP算法流程
目前TLP算法还没有正式的RFC协议,只有协议草案,下面介绍的算法流程为协议草案中的流程
1、在每次传出新数据的时候,如果满足下面的条件,则启动PTO定时器
连接处于Open状态
连接因为拥塞窗口大小受限而不能发出新的数据或者缓存中没有新的待发送数据(拥塞窗口我们后面介绍)
连续的PTO<=CON_PTO_SH,连续的PTO是指PTO超时触发loss probe报文的发送时候如果这个loss probe报文重启了PTO定时器那么就叫做连续PTO,其中协议草案中给出的CON_PTO_SH为2,但是协议草案指出实现上也可以使用CON_PTO_SH=1进行判断,linux使用的是CON_PTO_SH=1进行判断,也就是说linux中在发送loss probe报文的时候虽然不会重设PTO定时器,但是在收到ACK报文的时候仍然可以重启PTO定时器,最终结果就是可能会连续发送新数据包作为loss probe报文,但是如果loss probe报文为尾包的重传报文,那么只能发送一次而不能连续发送,后面介绍TLP与拥塞控制的时候会给出示例。
连接支持SACK功能
在满足上面这些条件后,判断如果当前发出去的还没有收到ACK确认的报文的个数(称呼为FlightSize)大于1,则设置PTO = max(2*SRTT, 10ms),如果FlightSize =1,则设置PTO =max(2*SRTT, 1.5*SRTT+WCDelAckT)。如果之前存在RTO定时器,则使用PTO定时器取代它并设置 PTO = min(RTO,PTO)。其中WCDelAckT代表延迟Ack定时器的最大值,之前我们说过TCP并不会对每个数据包都回复ACK,有可能会延迟等待一段时间和后面的数据包一起回复ACK报文,WCDelAckT就是延迟等待的最大时间。协议草案对WCDelAckT的建议值为200ms。
2、当收到ACK报文的时候
如果上面列出的条件仍然满足,那么在当前时间(即收到ACK报文)的基础上重新设置PTO定时器。如果上面条件不满足则取消PTO定时器。
3、当PTO定时器超时的时候
如果一个新的未发送的报文存在,发送新的报文。这个报文可能之前受到拥塞控制的限制而没有发送出去,linux在PTO超时发送这个报文的时候不会受到拥塞控制和Nagle算法的限制(后面文章会介绍拥塞控制和Nagle算法)。
如果没有新的未发送过的报文存在,则重传最后一个发送的报文。注意这里是发送最后一个已发送报文。可以参考后面的wireshark示例。
如果1中列出的条件仍然满足则重新启动PTO定时器,否则重新启动RTO定时器。linux中连续PTO<=1,因此此处总是会重启RTO定时器。
三、丢包探测(Loss Detection)
在拥塞控制里面,TCP发送端需要检测评估丢包情况来调整拥塞窗口,进而限制发送窗口的大小(后面内容会详细介绍,暂时只要知道拥塞控制需要检测评估丢包情况就行了)。在TLP算法流程里面我们介绍了当PTO超时时候可能会发送最后一个发出的数据包作为loss probe,但是如果这个数据包的初传丢失了,而loss probe的这次重传成功的时候,就需要一种探测算法来探测丢包。后面内容我们会介绍两种分别基于DSACK和FRTO的虚假重传检测,但是目前还有一部分主机并不支持虚假重传的检测,因此协议草案中给出的检测方式只是依赖RFC2018中的SACK,但是目前linux实现上已经支持使用DSACK来处理TLP的ACK报文了。假设连续发生了N次PTO超时而重传了N次尾包(对于linux因为不能连续PTO超时重传,N=1),假如接收端能收到N个TLP dupack(指ack number正好为TLP重传的recovery point,且不携带新的SACK块),则说明这N个尾包都到达了接收端,如果接收到的TLP dupack小于N,则说明可能发生了丢包,应该进行拥塞控制的处理。注意上面FlightSize=1时候,PTO的设置添加了WCDelAckT限制就是考虑到了延迟ACK的情况。对于接收端回复的ACK包丢失的问题,这个算法是保守的也是可接受的。
四、wireshark示例
1、tcp_retrans_collapse=0、tcp_sack=1、tcp_early_retrans=4、tcp_fack=1
在同时打开FACK和TLP的情况下,当loss probe触发的SACK块信息与ack number之间相隔3个以上(包括3个)TCP报文的时候就可以触发基于FACK的快速重传,如下图所示
server端传输No6-No10共5个tcp报文,在传输完No10报文的时候启动PTO定时器
client正常接收No6报文并回复不带有SACK信息的ACK报文No11,对于No7-No10直接丢弃做模拟丢包。
server端在接收到No11报文的时候,重启PPTO定时器为2倍RTT,从server端程序可以获取此时的RTT为500.503ms,因此PTO定时器大约为1s
PTO定时器超时,server端重传No10报文的数据,对应No12报文
client回复一个ACK确认包,同时含有SACK信息通知server端自己收到了序列号范围为(33-40)的数据
此时server端通过ack number得知client已经收到了No6报文,通过SACK信息得知client收到了No10报文,中间间隔3个TCP报文,达到了dup ACK门限,因此立即触发基于FACK的快速重传
2、tcp_retrans_collapse=0、tcp_sack=1、tcp_early_retrans=4、tcp_fack=0
接下来的这个示例No13之前(包括No13)的报文交互与上一个示例类似,但是因为这里关闭了FACK功能,在收到No13的时候并不能触发快速重传。但是因为PTO超时重传No12报文的时候初始化了RTO定时器,因此最终会RTO超时进行重传(No14-No16)。这里也可以看到linux中CON_PTO_SH这个参数为1。
如果CON_PTO_SH为2的话会在No12重传之后再次重启PTO定时器,接着PTO超时再次进行loss probe,此时连续2次loss probe,达到CON_PTO_SH门限重启RTO定时器。
3、tcp_retrans_collapse=0、tcp_sack=1、tcp_early_retrans=3、tcp_fack=0
在同时打开FACK和TLP的情况下,当loss probe触发的SACK块信息与ack number之间相隔3个以下(不包括3个)TCP报文的时候就可以触发基于增强ER的快速重传,如下图所示
server端发送No6-No9四个tcp报文,并在发出No9报文的时候启动PTO定时器
client正常接收No6报文,并回复不带有SACK信息的ACK确认包No10,No7-No9三个报文模拟报文丢失,client不回复ACK确认包
server端在收到No10的ACK确认包的时候,重启PTO定时器为2倍的RTT,PTO定时器超时的时候,server端进行loss probe发出No11数据包来重传No9数据包的内容
client收到No11数据包后,回复ACK确认包,并包含SACK信息通知server端收到了序列号为(25-32)的数据包。
server端收到No11的dup ACK后,判断满足ER重传的条件,启动ER定时器,定时时间为RTT/4,ER定时器超时之后server端发出No13和No14两个数据包,接着client分别回复两个ACK确认包。可以看到这里ER重传时候能够重传两个TCP报文,而上面FACK重传的时候只能重传一个TCP报文,这些都是拥塞控制的结果,我们后面内容会简单介绍的。
4、TLP与延迟ACK交互
TLP与延迟ACK的交互示例放到后续SWS的介绍文章中,作为与SWS综合示例的对照。
补充说明:
1、https://www.ietf.org/proceedings/84/slides/slides-84-tcpm-14.pdf
2、https://www.ietf.org/archive/id/draft-dukkipati-tcpm-tcp-loss-probe-01.txt 这个是linux实现的参考文档,里面有一些谷歌的TCP统计信息,有助于了解为什么我们会需要TLP功能。
3、linux对tlpack的处理可以参考tcp_process_tlp_ack,除了使用本文介绍的机制外还会使用DSACK来检测,后面文章会介绍DSACK相关内容。
4、TLP两个patch的log里面有对TLP代码的解释,可以参考