Google's BBR拥塞控制算法模型解析

0.模型

模型是最根本的!算法

我很是讨厌把全部的东西杂糅在一块儿,我比较喜欢各个击破,因此说,我最喜欢正交基!我但愿把待观测的东西分解成毫无耦合的N个方面,而后各自研究其特性。这个思路我曾经无数次提出,可是几乎没人会听,由于一旦分解,你将看不到目标,看不到结果,拆了的东西并不定能再装起来...使人欣慰的是,TCP的BBR算法思路也是这样,不幸的是,TCP领域的顶级专家并无N维拆解,人家只是拆解了2个维度。缓存

带宽和RTT BandWidth & RTT
我很惊奇Yuchung Cheng(郑又中)和Neal Cardwell是怎么发现这个正交基的,为何以前30年都没有人发现这个,最为惊奇的是,他们居然对了!他们的模型基于下图展开:网络

 

 

这张图几乎彻底描述了网络的行为!这就是网络传输的本质模型!之因此以前的Reno到CUBIC都是错的,是由于它们没有使用这个模型,我先来解释一下这个模型,而后再看看将Reno/CUBIC套在这个模型上以后,是多么的荒唐。工具

        值得注意的是,这个模型是Kleinrock & Gale早在1981年就提出来的,然而直到如今才被证实是有效的。以前的年月里,人们面临着实现问题(同时测量问题,后面会讲)。所以我把这个模型最终的实现者做为模型的主语,即Yuchung Cheng和Neal Cardwell们的模型,这并非说他们是模型的发明者。就相似牛顿的定律同样,其实伽利略已经提出了,只是没有用数学系统的论证并总结而已。性能

1.有破才有立

首先,咱们先要知道Reno/CUBIC错在哪里,而后才能知道BBR是怎么改进的。
        骨子里,人们总但愿时间成为横轴,除了生命和作爱,人类几乎全部的行为都是在于尽可能缩短期。
        效率是什么?人们只知道效率的分母是时间!人们一旦把时间肯定为横轴,不少时候就会蒙蔽不少真相。这是一个哲学问题,我之后再谈。
        人们在为TCP创建性能模型的时候,老是用”序列号-时间曲线“,”序列号RTT曲线“等来观察(好比Wireshark等分析工具),目标呢,很简单,就是查一下”一个链接最快能突突多少数据“,
        可是,这些都错了!Why?
        由于时间轴具备滞后性欺骗特征,全部基于时间轴的效率提高方案都是”先污染后治理“的方案!
        看一下上面那个模型图,全部30年来的拥塞控制算法的目标都是收敛于一个错误的收敛点(图的右边):不断地增长数据的传输,试图填满整个网络以及网络上的全部缓存,觉得这样就会达到比较高的带宽利用率,直到发现丢包,而后迅速下降数据发送量,以后从新向那个错误的收敛点前进,如此反复。这就是锯齿的根源!这个现象在以上的模型图上显示的很是明确,根本就不用解释。若是一开始人们就使用这个模型图,任何人都不由会问:为何要一直在警惕区域徘徊呢?正确的收敛点不该该是畅通模式的最右端吗??我以经典的VJ版TCP拥塞图来讲明一下:测试

 

 

咱们要作蜜蜂而不是老鼠。中北部省区的人们比较喜欢往家里屯菜屯肉,最后总有大量的食物坏掉形成浪费,看似吞吐很高,实际效率极低,而南方人则不一样,南方人历来都是买当天正好够吃的新鲜食物,毫不会往家里屯。形成这种差别的缘由主要是由于北方冬天很是寒冷,食物难觅,而南方则一年四季都不愁新鲜食物。若是咱们深刻一下考虑这个问题,正确的作法必定是南方人的作法,任何北方人屯食物并非说他就喜欢屯食物,他知道屯的食物没有新鲜的好,是出于没有办法才屯食物的,一旦条件成熟,好比大棚,温室出现,北方人也开始购买当天的新鲜食物了。
        TCP与此不一样,TCP的Reno/CUBIC并非由于条件不具有才往缓存里屯数据包的,对于能搞出CUBIC那么复杂算法的人,搞定BBR根本就是小菜一碟,我认可我看不太懂CUBIC的那些算式背后的东西,但我对BBR的理解很是清晰,像我这种半吊子水平几个月前就差一点实现了BBR相似的东西,但我绝对想不到CUBIC那么复杂的东西,之因此Reno/CUBIC被使用了30多年,彻底是由于人们一直觉得那是正确的作法,居然没有任何人以为这种作法是有问题的。
        基于{RTT,Delivery Rate}正交基的新模型一出来,人们好像猛然看到了事实的真相了:this

 


问题的根源在于,BBR拥塞控制模型和BBR以前的拥塞控制模型对BDP的定义不一样。BBR的模型中,BDP是不包括网络缓存的,而以前的模型中,是包括缓存的,这就是说,包括缓存的拥塞控制模型中,拥塞控制自己和网络缓存之间有了强耦合!要想作一个好的拥塞控制算法,就必需要完全理解网络缓存的行为,然而做为端到端的TCP协议,是不可能理解网络缓存的。因此一直以来,30年以来都没有出现一个比较好的算法,Reno到CUBIC,改进的只是算法自己,模型并无变!
        网络缓存是复杂的,有基于深队列的缓存,也有基于浅队列的缓存,无论怎样,都会遇到BufferBloat的问题,这是TCP所解决不了的,虽然如此,TCP仍是尝试填满包括这些永远琢磨不透的网络缓存在内的BDP,这个填满的过程是逐步的,开始于慢启动,而后...这个逐步填满BDP是两个过程,首先是RTT不变,逐渐填满不包括网络缓存在内的管道空间(在个人定义中,这个属于时间延展性的缓存空间),而后是逐渐填满网络缓存的过程(在个人定义中,这部分属于不带时间延展性的时间墙空间),问题处在TCP对后一个过程的不可知不可测的特性!!
        既然知道了Reno/CUBIC的问题根源,那么BBR是怎么解决的呢?换句话说,BBR但愿收敛在上图中红色圈圈的位置,它怎么作到的呢?
        列举作法以前,我先列举一下{RTT,Delivery Rate}正交基的美妙:spa

 

 

诚然,BBR的模型已经发现了在一个足够长但不太长的时间窗口内最大带宽和最小RTT是其收敛点,这就使得BBR有了明确的目标,那就是,求出最大的带宽,即Delivery Rate,以及求出最小的RTT,这个目标暂且搁置一下,先回答一个问题,为何采样时间窗口要足够长,是为了这段时间足以过滤掉假拥塞,毕竟时间能够冲破一切谎话,冲淡一切悲哀,那为何这段时间又不能过于长,由于要对网络环境的变化有即时适应性。基于此,BBR能够几乎能够抵御大多数的假拥塞情形了。
        最终,BBR的BDP以下图所示,再也不包括警惕区的网络缓存:.net

 

 

我用一个统一的图表示RTT和带宽的关系:orm

 

 

好了,如今我看能够看BBR到底怎么在这个新模型下探测最大带宽了。

2.BBR对最大带宽和最小RTT的探测

从模型图上能够清楚的看出如何探测最大带宽:

 

 

可是对于最小RTT的探测却不是很直观。
        在这里首先要谈一下BBR以前那些”基于时延“的拥塞控制算法。Reno/CUBIC属于基于丢包的拥塞控制算法,而像Vegas之类的属于基于时延的算法,其区别在于,Vegas对RTT的变化比较敏感,判断拥塞的要素是RTT的变化而不是丢包,无论哪一种算法,都须要外部发生一个事件,提示TCP链接已经处于拥塞状态了。事实证实,Vegas之类的算法工做的并很差,缘由在于它没法抵抗假拥塞,偶尔一次非拥塞形成的RTT增长也会引起TCP主动降窗,这种算法没法对抗共享深队列的其它基于丢包的TCP链接的竞争,所以没法广泛被采用。
        BBR须要测量最小RTT,可是它是基于时延的拥塞算法吗?
        并非!起初,当我第一次测BBR的时候,那是在国庆假期我醉酒后的次日,几乎动用了家里的全部设备(iMac一台,Macbook Pro一台,ThinkPad一台,ROOT-Android平板一个,刷过的荣耀立方一个,树莓派开发板一块,ROOT-Android手机一个,极路由一个,iPhone两个,iPad两个...),搭建了一个测试网络,让BBR和CUBIC,Reno一块儿跑,并制造了RTT突变,发现BBR并无降速,甚至对RTT的突变不会有反应,而对RTT的变化的剧烈反应是基于时延的拥塞算法的基本特征,但BBR并无!任何组合状况下BBR均可以完爆其它算法。这是为何?
        BBR在一个不随时间滑动的大概10秒的时间窗口中采集最小RTT,BBR只使用这个最小RTT计算Pacing Rate和拥塞窗口。BBR不会对RTT变大进行反应。可是若是整的发生了拥塞,RTT确实会变大,BBR怎么发现这种状况呢?答案就在于这个时间窗口的超期滑动,若是在一个时间窗口内持续没有采集到更小的RTT,那么就会将当前的RTT赋值个最小RTT。BBR就是这样抵抗假拥塞的。秒级的窗口内,什么都是瞒不住的。这就是RTT的测量以及使用原则:

 

 

如今能够明确,BBR的抢占性不糊由于其对时延的反应而下降,BBR不会对时延进行直接的反应。BBR没有抢占性,但也不示弱,它所作的是正确的作法所要求的,它只是作到了而已。
        以前在网上看到一个美国硅谷工做的实习生质疑BBR会由于时延反应而不利于公平性,我原本想回复的,后来感受翻次墙太麻烦了,

3.总结

咱们一直须要的是一个”可持续发展“的方案来解决效率问题。不幸的是,TCP出现的30年来,咱们见到的全部拥塞控制算法都是这种所谓的先污染后治理的方案,贯穿从Reno到CUBIC的全部算法。先把缓存填充到爆,而后再主动缓解,由于全部基于丢包的算法都在抢着填爆全部缓存,基于时延的算法太君子的主动降速行为就显得竞争性不够,典型的劣币驱良币的场景。
        我一直都觉得TCP的加性增,乘性减是一我的人为我,我为人人的策略,确实,能够这么理解,然而这是一种敬人一杯,自罚一壶的策略,结局就是广泛倒下...
        你们都能随口说出ssthresh的做用是什么,好比当窗口大于它就怎么怎么样,当小于它就如何什么的,可是有人知道它的含义吗?也许,你会发现ss是slow start的缩写,而thresh则是threshold的缩写,门限的意思,可是这不是正确答案,正确的答案在这里
The capacity of a path can be informally defined by the sum of unused available bandwidth in the forward path and the size of buffers at bottleneck routers.  
Typically, this capacity estimation is given by ssthresh.

真相如此晚近才被揭示,怪不得人们只知道ssthresh的用法而不知其含义啊!稍微详细一点的东西也能够看我写的一篇文章《TCP核心概念-慢启动,ssthresh,拥塞避免,公平性的真实含义》。
        事实上ssthresh定义了路径上全部缓存(包括时间延展缓存和时间墙缓存),因此说,当检测到丢包时,会将cwnd减小1/2并赋值给ssthresh,此时由于BDP已经满载,因此说其容量的1/2就是路径的缓存容量!Perfect!然而这是错的,TCP误觉得全部的缓存都是能够填充的,然而事实倒是,只有时间延展性质的缓存,即网络自己才是能够填充的,而时间墙缓存倒是应急的缓存,全部的TCP只要都避免使用时间墙缓存(包括路由器,交换机上的队列缓存),其才能真正起到应急的做用,带宽利用率才会最大化!
        应急车道是应急的,而不是行车的!
        BBR的新模型把这一切错误展现给了全部人,所以,BBR的指示是,保持最大带宽,并最小化网络缓存的利用。事实上,基于新模型额BBR要比以前的算法简单多了!千万不要以为新算法必定很难,相反,BBR超级简单。

        也许你也已经想到了BBR相似的思路,可是它可以在Linux上实现仍是要对Linux的TCP实现动手术的,并不只仅是一个拥塞模块那么简单,前几天的文章说了N遍,BBR以前的拥塞控制算法在非Open状态会被接管,再牛逼的算法也彻底没用,因为CUBIC试图填满整个包括队列缓存在内的全部缓存空间,在当下的核心深队列,边缘浅队列高速网络环境中,只有不到40%的时间内TCP的拥塞状态是处于Open状态,大部分状况,传统的算法根本就跑不到!好在BBR的实现中,做者注意到了这一点,完成了TCP拥塞控制的外科手术,快哉!

        BBR会在4.9或者5.0内核中成为默认的TCP拥塞控制算法吗?我以为可能还须要更多的测试,CUBIC虽然表现不佳,但起码并无由于其表现带来比较严重的问题,CUBIC的运行仍是很稳定的。可是我我的但愿,我但愿BBR赶忙成为全部Linux版本的标配,完全结束所谓TCP单边加速这个丑行!

附:时间延展性缓存和时间墙缓存

我一直强调BBR是简单的,是由于它确实简单。由于在上半年的时候,我差一点就想出了相似的算法,当时我已经准备对PRR作手术了。
        我以为拥塞控制算法对拥塞窗口的控制权不够大,我但愿用拥塞算法自己的逻辑来绝对窗口如何调整,而不是一味地PRR!我不相信检测到三次重复ACK就必定是发生了丢包,即使是在非Open状态,好比Recovery状态,我也但愿在个人算法认为能够的状况下能够增长窗口...我仔细分析了网络的特征,总结除了两类缓存:
带有时间延展性的缓存,即网络自己(确切的说是网线上跑的数据,从A到B须要时间,因此认为网络是具备存储功能的); 时间墙缓存,即路由器交换机的队列缓存。这类缓存的性质就是内存。