TCP拥塞控制及BBR原理分析

导语:TCP拥塞控制不仅仅是网络层的概念,可以将其归属于控制论的范畴。在TCP的演进过程中,出现了很多优秀的思想和算法,以实现网络传输过程中,在公平竞争性的前提下,尽可能地利用带宽资源。本文介绍TCP发展过程中出现的几种拥塞控制算法,并着重介绍BBR的原理。

一、拥塞控制概述

TCP拥塞控制不仅仅是网络层的概念,可以将其归属于控制论的范畴。在TCP的演进过程中,出现了很多优秀的思想和算法,以实现网络传输过程中,在公平竞争性的前提下,尽可能地利用带宽资源。

公平性

公平性是在发生拥塞时各源端(或同一源端建立的不同TCP连接或UDP数据报)能公平地共享同一网络资源(如带宽、缓存等)。处于相同级别的源端应该得到相同数量的网络资源。产生公平性的根本原因在于拥塞发生必然导致数据包丢失,而数据包丢失会导致各数据流之间为争抢有限的网络资源发生竞争,争抢能力弱的数据流将受到更多损害。因此,没有拥塞,也就没有公平性问题。

TCP层上的公平性问题表现在两方面:

(1)面向连接的TCP和无连接的UDP在拥塞发生时对拥塞指示的不同反应和处理,导致对网络资源的不公平使用问题。在拥塞发生时,有拥塞控制机制的TCP会按拥塞控制步骤进入拥塞避免阶段,从而主动减小发送到网络的数据量。但对无连接的数据报UDP,由于没有端到端的拥塞控制机制,即使网络出现了拥塞,也不会减少向网络发送的数据量。结果遵守拥塞控制的TCP数据流得到的网络资源越来越少,没有拥塞控制的UDP则会得到越来越多的网络资源。

(2)TCP连接之间也存在公平性问题。产生问题的原因在于使用了不同的拥塞控制算法,一些TCP在拥塞前使用了大窗口尺寸,或者它们的RTT较小,或者数据包比其他TCP大,这样它们也会多占带宽。

拥塞控制过程

拥塞控制主要包括四个过程:1)慢启动;2)拥塞避免;3)拥塞发生;4)快速恢复。

拥塞控制经典概念

RTT:数据包从发出去到收到对它的ack的来回时间,采用平滑方式计算RTT

RTO:重传超时。简单的如RTO=n*RTT, n=3(或其他RTO计算方法)

SACK:TCP Option携带多组ACK信息

FR:Fast Retransmission,收到3个dup ack后,即可认为发生了丢包。不需要等待RTO超时即可重传丢失的包。

ER:Early Retransmission,无法产生足够的dupack和没有新的数据包可以发送进入网络的情况下,减少触发FR的dup ack数量,以达到触发FR的目的。

TLP:如果发生了尾丢包,由于尾包后面没有更多的数据包,也就没有办法触发任何的dupack。实际上,Google统计超过70%的RTO是尾丢包导致没有任何dup

ack。TLP算法是通过发送一个loss probe包,来产生足够的SACK/FACK的信息以触发RF。

Pacing:控制发送速率,防止bursting

流控:Flow control站在单条TCP连接的维度,目的是让发送方发包的速度,不超过接收方收包的能力。所以流控解决的问题是,如何在接收方可承受的范围内,让单条 TCP 连接的速度最大化。通过滑动窗口机制实现。

拥塞控制:Congestion control站在整个互联网的维度,让网络里所有TCP连接最大化共享网络通道的同时,尽可能的少出现网络拥塞现象,让网络世界里的每一个参与者既公平又高效。

cwnd:发送窗口,拥塞窗口;在拥塞控制过程中窗口大小值变化。

rwnd:接收窗口,通知发送者能够发送的数据大小。

sliding window:滑动窗口,只是一种抽象机制概念;在发送请求及收到ack的过程中滑动。

二、TCP拥塞控制算法分类

历史上出现的各种TCP拥塞控制算法,其本质是针对拥塞控制的四个过程做策略调整。按照算法依据的因素,可以简单的分为以下类型:

基于丢包的拥塞控制:Tahoe、Reno、New Reno

因为Reno等算法是后续算法的基础,这里详细的描述下Reno算法的过程。

(1)慢热启动算法 – Slow Start

  • 连接建好的开始先初始化cwnd = 1,表明可以传一个MSS大小的数据。
  • 每当收到一个ACK,cwnd++; 呈线性上升。
  • 每当过了一个RTT,cwnd = cwnd*2; 呈指数让升。
  • 还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”。

(2)拥塞避免算法 – Congestion Avoidance 当cwnd >= ssthresh时,就会进入“拥塞避免算法”。算法如下:

  • 收到一个ACK时,cwnd = cwnd + 1/cwnd
  • 当每过一个RTT时,cwnd = cwnd + 1

(3)拥塞状态算法 – Fast Retransmit Tahoe是等RTO超时,FR是在收到3个duplicate ACK时就开启重传,而不用等到RTO超时。拥塞发生时:

  • cwnd = cwnd /2
  • sshthresh = cwnd

(4)快速恢复 – Fast Recovery

  • cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
  • 重传Duplicated ACKs指定的数据包
  • 如果再收到 duplicated Acks,那么cwnd = cwnd +1
  • 如果收到了新的Ack,那么,cwnd = sshthresh ,然后进入拥塞避免算法。

Reno算法以其简单、有效和鲁棒性,应用最广泛。该算法所包含的慢启动、拥塞避免和快速重传、快速恢复机制,是现有的众多算法的基础。从Reno运行机制中很容易看出,为了维持一个动态平衡,必须周期性地产生一定量的丢失,再加上AIMD机制–减少快,增长慢,尤其是在大窗口环境下,由于一个数据报的丢失所带来的窗口缩小要花费很长的时间来恢复,这样,带宽利用率不可能很高且随着网络的链路带宽不断提升,这种弊端将越来越明显。另外,丢包并不一定是网络拥塞,可能是网络常态,但是基于丢包的拥塞控制并不能区分。

基于时延RTT的带宽预测:vegas

vegas通过对RTT的非常重的监控来计算一个基准RTT。然后通过这个基准RTT来估计当前的网络实际带宽,如果实际带宽比我们的期望的带宽要小或是要多的活,那么就开始线性地减少或增加cwnd的大小。

中间路由器缓存数据导致RTT变大,认为发生拥塞;RTT不公平性,当不同的数据流对网络瓶颈带宽进行竞争时,具有较小RTT的TCP数据流的拥塞窗口增加速率将会快于具有大RTT的TCP数据流,从而将会占有更多的网络带宽资源。

基于丢包和RTT:westwood

在发送端做带宽估计,当探测到丢包时,根据带宽值来设置拥塞窗口、慢启动阈值。 那么,这个算法是怎么测量带宽的?每个RTT时间,会测量一次带宽,测量带宽的公式很简单,就是这段RTT内成功被ACK了多少字节。Westwood会根据RTT变化来判断丢包是否是网络拥塞造成的,还是网络常态的丢包。如果时延变化不明显,就认为是非网络拥塞,此时cwnd减少的比较小。

二分搜索最佳cwnd:BIC-TCP

BIC-TCP是Linux 2.6.18默认拥塞控制算法,依赖丢包条件触发。BIC-TCP认为TCP拥塞窗口调整的本质就是找到最适合当前网络的一个发送窗口,为了找到这个窗口值,TCP采取的方式是(拥塞避免阶段)每RTT加1,缓慢上升,丢包时下降一半,接着再来慢慢上升。BIC-TCP的提出者们看穿了事情的本质,其实这就是一个搜索的过程,而TCP的搜索方式类似于逐个遍历搜索方法,可以认为这个值是在1和一个比较大的数(large_window)之间,既然在这个区间内需要搜索一个最佳值,那么显然最好的方式就是二分搜索思想。

BIC-TCP就是基于这样一个二分思想的:当出现丢包的时候,说明最佳窗口值应该比这个值小,那么BIC就把此时的cwnd设置为max_win,把乘法减小后的值设置为min_win,然后BIC就开始在这两者之间执行二分思想–每次跳到max_win和min_win的中点。

BIC也具备RTT的不公平性。RTT小的连接,窗口调整发生的速度越快,因此可能更快的抢占带宽。

连续拥塞间隔:CUBIC

CUBIC在设计上简化了BIC-TCP的窗口调整算法,在BIC-TCP的窗口调整中会出现一个凹和凸(这里的凹和凸指的是数学意义上的凹和凸,凹函数/凸函数)的增长曲线,CUBIC使用了一个三次函数(即一个立方函数),在三次函数曲线中同样存在一个凹和凸的部分,该曲线形状和BIC-TCP的曲线图十分相似,于是该部分取代BIC-TCP的增长曲线。另外,CUBIC中最关键的点在于它的窗口增长函数仅仅取决于连续的两次拥塞事件的时间间隔值,从而窗口增长完全独立于网络的时延RTT,使得连接之间保持良好的RRTT公平性。

来看下具体细节:当某次拥塞事件发生时,Wmax设置为此时发生拥塞时的窗口值,然后把窗口进行乘法减小,乘法减小因子设为β,当从快速恢复阶段退出然后进入到拥塞避免阶段,此时CUBIC的窗口增长开始按照“凹”式增长曲线进行增长,该过程一直持续直到窗口再次增长到Wmax,紧接着,该函数转入“凸”式增长阶段。该方式的增长可以使得窗口一直维持在Wmax附近,从而可以达到网络带宽的高利用率和协议本身的稳定性。

CUBIC窗口的增长函数:W(t) = C * (t-K)3 + Wmax, 其中C和β为常量。

t为当前时间距上一次窗口减小的时间差,而K就代表该函数从W增长到Wmax的时间周期。

通俗一点讲,假如我们知道了Wmax,那么CUBIC的核心思想就是需要在连续两次拥塞期间执行完上面的三次函数增长曲线

基于精准带宽计算:BBR

BBR通过实时计算带宽和最小RTT来决定发送速率pacing rate和窗口大小cwnd。完全摒弃丢包作为拥塞控制的直接反馈因素。

三、BBR算法原理分析

传统的拥塞控制算法是计算cwnd值来规定当前可以发送多少数据,但是并不关注以什么样的速度发送数据。如果简单而粗暴地将窗口大小(send.cwnd、recv.cwnd的最小值)数据全部突发出去,这往往会造成路由器的排队,在深队列的情况下,会测量出rtt剧烈地抖动。bbr在计算cwnd的同时,还计算了一个与之适配的pacing rate,该pacing rate规定cwnd指示的一窗数据的数据包之间,以多大的时间间隔发送出去。

基础概念

  • BtlBW:最大带宽
  • RtProp:物理链路延迟
  • BDP:管道容量,BDP=BtlBW * RtProp

我们知道,网络工作的最优点是在物理链路延迟状态下,以最大速率传输数据。传统的拥塞控制算法思想是根据数据传输及ACK来确定RTT,但是这个RTT并不是物理链路延时,可能包含了路由器缓存耗时,也可能是拥塞状态下的耗时。传统的带宽计算也是在不断的试探逼近最优发送窗口,并在RTT或者统计周期内计算带宽。这种情况下,RTT并不是真正的物理链路延迟,带宽也有可能是在有路由缓存或丢包状况下计算得到,那么必然得到的不是精准的值。

BBR摒弃了丢包和实时RTT作为拥塞控制因素。引入BDP管道容量来衡量链路传输水平。BBR追求的是在链路最小RTT(物理链路延迟)的状态下,找到最大带宽。

BBR算法理论基础

首先我们认为网络最优点是可以达到的。下面描述RTT及收包速率与数据包投递速率的关系。

图中上半部分的过程可以描述为:随着数据包投递速率增加,如果没有超过最优带宽,则RTT不会变化,此时的RTT是物理链路延迟。随着投递速率继续增加,这时中间路由节点可能出现需要缓存数据包的情况,这会导致RTT变大。如果投递速率继续增加,超过路由缓存能力,则可能出现丢包。

图中下半部分的过程可以描述为:随着数据包投递速率增加,如果没有超过最优带宽,则发送方确认接收端收到的数据速率增加。随着投递速率继续增加,因为数据包缓存在中间路由,这些包并不能及时得到ACK,因此发送方得到的ACK速率,即发送发确认接收方收到数据的速率会维持不变。如果投递速率继续增加,超过路由缓存能力,则可能出现丢包。

带宽计算

1)应答了多少数据,记为delivered; 2)应答1)中的delivered这么多数据所用的时间,记为interval_us。 将上述二者相除,就能得到带宽:bw = delivered/interval_us;该计算方法不关注数据包ack及顺序,是纯粹的标量。

我们可以根据图示很容易算出从Delivered为7时的数据包被确认到X被确认为止,一共有12-7=5个数据包被确认,即这段时间网络上清空了5个数据包。我们便很容易算出带宽值了。

ProbeBW:探测最大可用带宽

  • BtlBW * Pacing_Gain,每次增加码率是之前的25% per RTT
  • 10s探测一次带宽

ProbeRTT:探测最小RTT

当10s内没有发现最小RTTProp时,就要进入ProbeRTT状态。在ProbeRTT状态,仅发4MSS/RTT(接近停止发送),从而排空链路上的数据包,测量真实的RTTProp。这里带来的一个问题是,在一个RTT时间内以4MSS速率发送可能会造成抖动,特别是长RTT场景。具体的参考willko文章《GBN手札-BBR实时大数据传输之痛》。

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计