...
我本来是答应温州皮鞋老板写这么一篇文章的,但拖到了现在才写。PS:下山的路上,我和小小均唱了一首江南皮革厂倒闭了,而且还被录了下来!明日入川蜀,进藏。唉,这个春节假期我只能休息这么几个小时,却利用这时间写了这篇文章(我是从来不过春节的)。
本文是我的另一篇文章《令人躁动一时且令人不安的TCP BBR算法》的后续,可以先看这篇文章,便大致可以了解我的观点。我也承认,关于BBR算法:
一开始我是膜拜:哇,BBR解决了所有的问题,天下第一,怎么可以这么伟大!
然后是理性地崇拜:嗯,BBR确实不错。
然后是有那么一点失望:BBR试图解决Bufferbloat,但是没有解决好。
然后是漠然:BBR在大多数情况下还不错,比别的算法好。
然后看到了点瑕疵:BBR在深队列情况下竟然被Scalable算法完爆了...
然后详细解剖了SDN,B4以及Chinanet和CN2...
最后,经大师指点,我才觉得Google的BBR就是一个坑!
这是一篇BBR的科普!
这是一篇BBR的科普!
这是一篇BBR的科普!
声明:
本文仅仅针对Linux内核中的TCP实现进行讨论。Reno,CUBIC等并非表现不佳,而是因为它们如今略显不合时宜。十年前甚至三十年前的“不能这也不能那,建议这样以及建议那样”可能并不适合当代,但同时我也不能否认其它闭源的系统上存在有基于Reno,CUBIC修改而成的其它拥塞算法,这些算法并非按照标准的要求和建议实现。
PS:TCP在三十年来,一直都在不断进化,性能可能不高,但可用,且足够公平,三十年前适应了当时网络,然后同网络技术同时进化,如今的TCP依然是可用的。
前言:
BBR在4.9内核被发布了,发布后人们看了代码后不禁要问,这么简单的东西--BBR在算法上没有那些复杂的数学推导(除非你对那些参数做仔细的推敲),为什么之前没有人想到呢?Linux内核的代码是开源的,任何人都可以随意改它,即便不被社区接纳,起码可以自用啊!我觉得像AppEX,网宿,以及BAT等这种“认为‘费厄泼赖’应该缓行”的公司肯定是有自己的修改,只是不想公布出来而已,它们做的事情无非就是让CUBIC不再按照文档上规定或者建议的那般运行,而是添加自己的哪怕是乱七八糟的逻辑。我觉得吧,这种行为,几乎都是搞好了,损人利己,搞得不好,则损人不利己...
BBR使用rate_sample获取了很多的信息,闭源的TCP实现我想也并非不是这样,难道BBR之前的Linux内核就没有这个能力吗?答案非常明确,不是Linux内核没有这个能力实现BBR,而是在看似到处掣肘的前BBR TCP实现中,蕴含着一些不便明说的道理。孙逸仙说过的话,民智未开的时候,必须要经历训政,才可以实施民主。按照这个观点,BBR背后的想法有点过于激进了。BBR的要旨在于它用一个cong_control回调函数接管了一切,避开了PRR,所以它才可以在公敌宣告面前依然威风凛凛!在网络上,只能看BBR的“人品”了,好在,BBR的人品还不错!
这是Google家的东西,难道你不跪添?!完全正确,Google简直控制了一切,他家的东西大多数人都会跪舔的,因为“他家的东西都是对的”。2005年的时候,有人(未经该人同意,我无权透露更多信息,但所述绝对真实,若征得此人同意,我定会为其再次击鼓)提出一个和BBR几乎完全类似的东西,连个专利都难申请下来(写这句的含义在于,我个人认为专利是很Low的东西,特别在国内),更别提推广了。如今呢?Google提出了BBR,引导大家开启一轮跪地开瓶盖的行为,而且是集体行为,噫吁嚱!
Google竟然把BBR算法并入内核mainline了!毫不夸张,Google的任何东西都会并入的,PRR,ER,RACK,NV,...这些都是Google可以让人跪地开瓶盖的东西,那么,你,跪舔了吗?我,是跪舔了!但是以后是不会了。
关于本文的目的
写作本文纯属闲着没事干,我一直希望写一篇描述BBR算法的科普文章,并不是想让任何人都能看懂,而是怕我自己在若干年后再次涉足这一块的时候,可以不求甚解地快速领略,但看来我还没有那个能力。自己会是一方面,表达出来让别人也会则是另一方面。所以,本文的目的不是任何人都能看懂,只限于知道BBR的“内线”能看懂即可,这里面也没有什么新的东西,只是一篇描述BBR的软文,也算是科普性质吧。
--------------------------
补充一点,很多人问到了关于TCP传输的优化。
最简单的方法,那就是结合BBR和CUBIC,Reno,CDG等算法,互补其长短。本文当然不讨论这些细节,因为我也不知道,就算我知道,我会用我知道的东西去换取更多的荣誉或者报酬,而不会在这里写博客。所以点到为止即可,也许还有些点是错的,我的目标是,引发思考!
BBR之前的算法是什么首先要澄清的是,即便在BBR之前,很多的公司或者个人就已经在类似CUBIC之外添加自己的东西了,没有任何人阻止你这么做,所以我的下文将排除针对这种做法的叙述,仅仅针对Linux内核主线版本的TCP拥塞控制算法做一番简述。
--------------------------
总体上的
BBR背后是一种自主的拥塞控制机制,与此相反,传统的拥塞控制算法则是相对盲目的基于事件的被动反馈。这两者之间的差异决定了它们在性能上的表现。我再重复一遍:
BBR以及之后:自主地控制
BBR之前:被动地反馈
接下来,我先从显而易见的视角来解释一下。
如果你了解到的信息越多,你就越不容易被欺骗,相反,如果你被告知不允许了解任何信息,那么你唯一可以依靠的只有信任本身,如果这点不能保障,你就很容易被欺骗,这个话题不便深究,容易被查表,还是回到TCP本身为好。接下来我以最简单的Tahoe算法代指BBR之前的任何算法。
Tahoe除了可以获取两个信息之外,几乎对外界毫无感知能力。这两个信息是:
ACK的到来
测量得到的RTT
Tahoe必须依靠这仅有的两个信息完成所有的事情,并且还不能造成连接崩溃...这有什么难度吗?其实也不难,所有传统拥塞控制算法大体思路基本一致:
针对“ACK的到来”--试探性地增加发送量
针对“测量得到的RTT”--用此RTT估算出一个大致的间隔,即RTO,超过RTO会认为数据包丢失,重传之,将发送窗口减少到1。
在以上两点之间不断循环,这构成了一个典型的反馈系统,为保证公平性,AIMD是必然的,这“几乎”就是TCP的全部了。虽然后面有了Reno,NewReno,Vegas,CUBIC,TLP,RACK等“越来越先进”的机制,但是本质上它们全部都是围绕上述两点的优化,在拥塞控制的机制上,并没有什么突破性的进展。
这里的重点并不是这些算法细节,说实话懂这些细节没个鸟用,这里的重点在于,这套机制非常容易使TCP连接受到欺骗!
我们反问一个问题:不管采用任何手段检测到了丢包,一定要大幅度降低数据的发送量,执行乘性减窗吗?为什么非要这么做呢?
回答似乎是可以脱口而出的,因为要保证公平性,必须执行“乘性减”...这个回答是完全错误的!有什么证据表明如果不执行乘性减就会损害公平性呢??直说吧,如果一次丢包是因为链路噪声的原因,比如信道信号干扰损耗导致的,而不是由于被路由器交换机丢弃的,你执行乘性减无疑是自废武功啊!可悲的是,你对于区分这个场景无能为力!造成这个局面的根源是什么?
信息不足!
那么,需要哪些信息可以区分出上述的丢包场景呢?
几乎不变的RTT
几乎不变的传输速率
不幸的是,Linux传统的TCP实现不足以让你获取这两个信息,相反,其引入的复杂性让事情变得更加糟糕!不光无法区分噪声丢包和拥塞丢包,还引入了复杂的乱序判断和处理。
其实,这不是TCP本身的错,这只是Linux的实现上欠妥。没有任何理由仅仅为拥塞控制算法逻辑传递一个ack号参数,但木已成舟,在BBR之前,Linux所有的拥塞控制算法框架均可表示如下:
正常状态,收到ACK--按照某种大体是加性的方式增加窗口。
检测掉丢包--按照某种大体上是乘性的方式减少窗口,并进入异常状态。
异常状态,收到ACK--判断是否被判为丢失的数据包已经传输完毕,如果是,进入正常状态。
记住,BBR之前所有的拥塞控制算法都是以上这个逻辑!因此,我给出本文的第一个状态机:
这个状态机运转在“几乎”整个互联网的端节点上,被各种AQM机制无情的惩罚的同时,又被低质量的链路噪声有意的戏弄!
只要有链路噪声造成的丢包,状态机就会使端节点在本不该进入异常状态时错误地进入异常状态!结果不是避免了拥塞,而是浪费了带宽!
--在不该降窗的时候,发生了乘性降窗!
--------------------------
我不敢说互联网上所有的端主机都在运行上段中的状态机,因为我知道部署了AppEX软件的主机肯定跑的不是上面的状态机,并且我猜测微软的系统里也不是跑的上面的状态机,此外,本文写作的时候,BBR已经发布一段时间了,我相信很多主机都已经升级了内核并且部署了BBR算法,BBR算法显然跑的也不是上面的状态机。
--------------------------
BBR采用了不同的状态机,取消了异常状态。注意,取消异常状态是因为BBR已经不需要异常状态,它根本就不Care丢包。
在BBR的逻辑里,它会实时地检测当前的传输带宽,从而据此计算接下来的发送速率,这几乎就是BBR的全部。下面我尽可能简单地描述一下BBR的细节。
BBR如何避免排队
说实话,BBR做不到避免排队!
BBR试图主动有意识的避免了排队(但仅仅是试图!)。这是通过测量并使用最小RTT做到的。我们知道,排队意味着RTT的增加,结论就是RTT越小,当前链路上的排队的数据就越少,当RTT小到不能再小的时候,意味着链路队列被清空,即没有队列。BBR即使用这个“小的不能再小”(这个受传输的固有延迟限制,除非超越光速!)的最小RTT(min_rtt)是否可以维持来判断采集到的实时传输速率(bw)是否在不排队的前提下足以维持。
但是这并不是说使用BBR算法的流就不会排队,因为这不是一个流的控制算法所能控制的!BBR仅仅是在根据以下原则来判断是否有排队发生的:
带宽的变化--通过增加发送量,在下几个采样周期测量所得的传输带宽保持恒定而不再增加。
RTT的变化--在一段相当长的时间段内,RTT持续增加,而从来不曾减少!
如果链路上已经出现了排队,BBR是无法发现的,除非队列清空或缩减,BBR会一直觉得链路是畅通无排队的。这就是BBR的问题之所在,BBR甚至都无法发现这个问题。正如一个北京人到了上海,会发现上海南北高架很畅通一样,这只是相比北京而言的,事实上,深圳的滨海大道会更畅通,只是他从来没有来过而已。这永远是一个无法调和的矛盾。
BBR为了兑现诺言,只能将“自己在维持自己测得的最小RTT的情况下测量所得的带宽”作为真实可用的带宽,本质上就是,不管怎样,我决不会添堵!不堵的时候,我不会制造拥堵,已经拥堵的时候,我不会添堵。
Vegas算法的问题
在我们知道了BBR并无法避免排队的前提下,退而求其次,不会添堵。但这样是不是很吃亏呢?
是的,非常吃亏!
Vegas就是因为这个而退出TCP的历史舞台的,其实,Vegas是个很优秀的算法,至少我个人是很喜欢的。但是Vegas算法太过教条了,它只要探测到RTT的增加,就会认为发生了排队,从而主动减少数据的发送量,即减窗,虽然还不至于乘性减窗,但至少是减窗了。后果就是自己把带宽让给别人了,让给那些“使用基于丢包算法的TCP流”了,在路由器缓存很大的情况下,只要那些流疯狂占据缓存但还不至于丢包,这些带宽就不能被Vegas流所用,因为正是因为路由器缓存被占据了,造成了所有流的RTT增加,而Veags会因为RTT的增加而降窗降速!这绝对是一种“劣币驱良币”的过程!最终会导致Vegas的君子行为销声匿迹!
君莫舞,君不见玉环飞燕皆尘土
BBR解决了Vegas的问题,虽然不是彻底解决,至少是在浅队列场景下解决了Vegas的君子式退让问题。
很多人认为BBR是一个基于时延的算法,我并不认同,它只是利用了时延,至少是奉承或者迎合了AQM系统,BBR和路由器AQM的关系正如罗马共和国末期后三头同盟时期屋大维和元老院的关系一样!
【我本来是想在这里穿插一段屋大维和元老院(西塞罗和其他人),安东尼,布鲁图斯之间周旋的故事呢,比如穆提那那段特别精彩】
既然BBR不是基于时延的算法,那它是什么?
BBR是基于“时间段内时延”的算法!
好吧,我承认有点玩文字游戏了,但不可否认,这“时间段内”四个字之差,效果却大不同!古话说“事不过三”,但这个太笼统,不好理解,我以婚姻为例。作为气话,一般而言夫妻在吵架的时候,都会无意间说出离婚两个字,但是几乎都不会离婚!因为多年的夫妻关系在事后都会100%把之前那次吵架判断为是“噪声”!这有个前提,就是夫妻有冷静下来的时间,如果说必须在吵架的当时就要做出决定,那几乎不会有超过一年的婚姻。Vegas算法就是那种必须在“吵架当时做出决定”的算法,一旦发生RTT增加,就马上降窗,而BBR则是真正的现实中的夫妻生活。
不管是夫妻之间,还是在BBR和CUBIC之间,都是博弈的双方。在博弈的过程中,一方总是在等待对方采取某种“示意悔改”的行为或者动作,遗憾的是,这是不对称的,夫妻之间,几乎总是男人先示弱,而在TCP算法之间,BBR几乎总是可以赢。它是怎么做到的呢?
跟Vegas不同,BBR在发现RTT增大时,不会马上降速,而是继续保持之前几个采样周期内采集到的最大速率进行数据发送。BBR在等,在等什么呢?
这是一种乐观的等待,这是一种“君莫舞,君不见玉环飞燕皆尘土”式的等待,BBR是如此的自信,如此的自信在等着那些CUBIC之流“出事”。因为CUBIC在尽情“挥舞”之后,必然会将数据填满队列而丢包,一旦丢包,它们无一例外会将窗口降低到整个BDP的0.7左右的大小,这是其拥塞算法的逻辑决定的,这意味着会有大量的带宽会被清空,而这正是BBR所期待的。RTT重新减小,BBR又可以毫无负担地依照原先的最大采集带宽发送数据。在这个过程中,BBR丝毫不在乎丢包,它将正常数据和重传数据一起对待,统一填充整个BDP。
这种“时间窗口内”的最小RTT,其思想是与之前的各种RTT平均值完全不同的,窗口内的最小RTT是一种主动的容忍,窗口的宽度即表示一个最大容忍度,而计算RTT的平均值则只是一种鸵鸟行为,一种掩耳盗铃的行为,这是相当消极的反应。
BBR带宽测量
最小RTT的测量,我已经描述过了,它无非是用一个时间段内的RTT最小值替代了瞬时值(或者某种平均值...),以此期望在这个时间段内对方发生一些“不愉快”的事情,以此来收敛。然而,有时候,不愉快的事情也会在自己身上发生,比如对方端主机接收能力下降这种,这会造成发送端采集到的传输带宽变小,与RTT的测量类似,BBR不会相信一次的结果,在若干次采样之后,才会放弃之前的采样结果。
与上一节整合。BBR针对采集到的传输带宽和采集到的RTT分别维持一个时间窗口,缺省情况下,传输带宽采用连续10个的采样周期作为窗口大小,而最小RTT则采用10秒的时间作为窗口大小。如下图所示:
注意,这并不是为了平滑掉瞬时噪声!其实,瞬时噪声更不没必要去平滑,不要掩耳盗铃,自欺欺人。你要做的,只是区分这真的是噪声,还是常态。
BBR带宽以及最小RTT不可同时测量
BBR的PPT和论文上说,其最大带宽和最小RTT是不可同时测量的,为此我本人也基于其数学模型写过一篇理论分析《Google's BBR拥塞控制算法模型解析》,但我不认为所有这些是易于理解的,所以说我在本文中再多写几句。
.测量最大带宽:为此,我们必须不停地加量,直到丢包为止。链路的队列缓存是不可避免的,持续加量必然引发排队,所以只有到至少引发排队才会探测到最大带宽。
.测量最小RTT:为此,我们必须保证绝对不能排队,因为排队会在RTT中增加队列延迟,而这部分延迟本来是可以通过避免BufferBloat而避免的。避免之后,RTT会减少到最小的纯粹传输延迟。
所以说,一方面最大带宽必须要排队才能测量得到,而最小RTT必然不能排队,所以二者无法同时测量。
本节结束!
相比Tahoe,BBR新的状态机
是给出本文第二个,也是最后一个状态机的时候了:
我们可以认为,BBR几乎总是运行在ProbeBW状态,除非其它的流对队列缓存抢占的太密集太激烈,造成了全局同步抢占,否则BBR总是可以等待到其它的流(不使用BBR的流)“出事降窗”,从而腾出更多的带宽,促使RTT下降,避免BBR进入ProbeRTT状态。
Pacing控制之于窗口控制
Pacing控制和窗口控制的区别在于,前者是在时间轴上均匀发送数据,而后者则是一次性突发数据,图示如下:
但是你可曾考虑过以下的问题:
这个问题并不是BBR的问题,而是所有使用Pacing发送的TCP流均固有的问题,现在,我们暂且把这个问题搁置,来考虑几个现实的问题吧。
BBR的优势之-抢占快
以上的那个BBR状态机有点简化了,在ProbeBW阶段,BBR并非简单的维持着窗口内的最大传输速率进行数据的发送,在某些时刻,BBR会在窗口内最大传输速率的基础上试图增加固定比例的发送速率,以此来探测是否有新的空闲带宽。
如果有新的空闲带宽,那么新的,更大的传输速率马上就会反馈回来,从而更新窗口内的最大传输速率,如果没有更多的剩余带宽,BBR则会在下一个阶段把本阶段为了探测带宽而增加的发送速率降下来到合理的水平。
为此,BBR在ProbeBW阶段维护了一个循环运转的档位周期,来实现在固定的间隔内探测新的空闲带宽或者排空已经透支的带宽:
这里要说的是,缺省情况下,在档位周期的ProbeMore阶段,每次试图增加原有最大速率25%的速率,这确实是个比较大的值了,且是乘性的增加,一次性增速到不能再增为止,令人惊叹。我们对比Reno,甚至CUBIC就会知道,它们在每收到ACK反馈的时候,均是以线性,至少是准线性的,即便是这种线性的加性增窗,一旦占满了全部队列缓存,发生了丢包,这类算法马上就会执行乘性减窗,美其名曰为了公平性,实则不得已而为之啊!如此谨慎的探测,其成本也是如此高昂,具有讽刺意味的是,传统意义上,在几乎三十年间,TCP的各种拥塞控制算法正是靠这种罪与罚的交替而维持平衡收敛的!
BBR的优势在于,这种探测几乎是零成本的,如果探测成功,那么BBR将会占据新的带宽,如果探测失败,BBR将会在下一个ProbeDrain阶段把这些多出来的数据排空从而避免丢包。为什么BBR不用经历罪与罚,或者主观的说,为什么不用先污染后治理呢?
BBR的优势之-与AQM的关系
BBR知道任何当前所采取行为的后果,并且能为此后果负责,因此,按照善意的观点,BBR主观上就不会造成“污染”,并不会“犯罪”,因为它知道,主动地占据队列缓存的后果就是污染网络,从而接受惩罚--丢包!
BBR的行为是一种不损人不利己的行为,如果它明知会占据缓存还主动占据,那将是一种损人不利己的行为,哪个是可取的呢?我想任何人都会明白吧。只是,Reno以及CUBIC之类的算法并不能择善而为,这并不意味着它们是有恶意的,只是因为它们的机制所依赖的信息不足以让其对网络环境有足够的认知(参见本文第一个状态机图示)。所以说,为了高效斧正这些算法,必须让路由器帮忙,这就是AQM(主动队列管理)存在的理由!
起初,路由器在缓存排满的时候,会丢弃队列尾部的数据包,这会导致只有队列完全满了才会发生丢包,丢掉尾部的数据导致整个反馈链会断裂(TLP一定程度上弥补了一些损失)...这是路人皆知的事实。更加复杂的AQM会在队列将满未满的时候,随机丢弃一些非尾部的数据,这样对于一个流而言,被丢弃的数据包后面的数据仍然会到达接收端,从而为发送端带回SACK反馈以触发快速重传而非等待超时!
这么复杂的机制难道就是为了“在特定的场合丢弃一个数据包从而触发发送端减窗降速”吗?是的!由于发送端除了丢包探测之外没有别的手段(忘了Vegas吧,但请重新审视New Vegas),只能由路由器的队列管理机制来变相斧正。假如在没有发生拥塞的时候,检测到了丢包,那只能说可能一条老化的线缆欺骗了你!
BBR还需要这些吗?理论上不需要了,因为它自己可以知道一切。一条老化的线缆无法欺骗你说发生了拥塞,你也不会因为一条老化的线缆丢失了数据包而执行拥塞后的乘性降窗。
BBR的劣势之-减速收敛慢
在任何时候,当你主动增速的时候,你很容易知道是否有空闲的资源,只要你想增速,并且反馈的速度确实增上去了,那说明你成功了,如果你只是想增速,然后却增不上去,那就说明你失败了,这很容易!所有堵在路上的司机都想增速,但几乎都会失败。但是对于降速却没有这么简单!因为,只要你想降速,那么速度几乎一定会降下来。这正如求生的办法只有一种,死法却成千上万一个道理,想活着可能并不由你决定,但是想死却完全由你决定。
BBR不知道是不是降速正合时宜,所以在ProbeDrain阶段会比较谨慎。在一个档位周期内,只会最多降速25%,然后希望再次降速,只能等待下一个档位周期。这是和BBR的ProbeMore完全相反的,后者会在一个档位周期内完全增速到不能在增为止。
参见我之前的文章《令人躁动一时且令人不安的TCP BBR算法》,文中说ProbeDrain的缓慢执行将可能会造成比较大量的丢包。
不管怎样,罪与罚这一次降临到了BBR头上,不收敛或者收敛慢即是罪,罪必被罚。
结合上面抢占快以及这里的收敛慢的描述,你也许看得出这是一个MIAD的控制机制,与AIMD完全相反,这难道不是违背了控制理论公平性的定理吗?其实什么也没有违背,BBR为了公平性而做的必要收敛行为并没有表现在针对反馈信息的反应行为上,而是包含在其主动不添堵的速率计算过程中了,试想,如果你本来就知道该发送多少数据,你干嘛还要执行AI过程增窗探测呢?BBR不需要AI,因此也不会MD,它看似可以在它内在的“档位周期”循环中搞定一切,但是有时候,还是会吃亏的...
BBR的劣势之-深队列依然会饿死
BBR几乎被推上神坛,好像其会永久保持胜利。但也不尽然,BBR有时是不可能赢的。
起初,有人说CUBIC之类的算法会饿死BBR,对此我并不同意,虽然我明白这些人可能已经看到了BBR的问题所在,我的意思是,大多数情况下,BBR是可以胜利的,这依赖与它对信息的全面掌握。但信息并不是争斗的全部。所谓知己知彼百战不殆也仅仅局限在战略意义上。我们先在感官上有一个浅浅的认知,如果你对你自己的状况十分理解,身高150cm,体重100斤,握力握不住一只鸡...你的对手身高200cm,体重200斤,一拳可以干死一头牛,你知己知彼,论打架,你能三局一胜吗?
在队列缓存比较大的时候,BBR将“等不到Reno,CUBIC之类的流出事而降速”,深队列将会让努力不排队不丢包的BBR耗不起。
BBR维持一个比较高的速度来发送数据,此时来了CUBIC流,在它占据缓存到丢包之前,BBR流的速率是逐渐降低的--这是因为队列被CUBIC流所占,BBR包之间被CUBIC包插队,RTT是逐渐升高的--这是因为CUBIC流排队增加了排队延迟。BBR此时在等待队列被填满后CUBIC乘性减窗,但是缓存实在太大了,在队列填满之前,BBR的缺省10个采样周期带宽窗口已经滑过去了几个,窗口内留下的是一个又一个减小的带宽,同样,只要在缺省的10秒内,RTT会一直持续增加,结局就是,BBR带宽在持续减少。
整个过程的理解其实非常简单,BBR是可以主动不占据队列缓存的,虽然它在降速前还会有一个忍耐期(即窗口的大小),但毕竟忍耐期限是固定的,CUBIC之流如果在这个期限内还没有“出事”,那么BBR必然会退让!我们知道CUBIC之流出事与否,完全取决于链路噪声和路由器队列缓存的深度,因此我们可以有一个推论,那就是在低噪声链路中,如果转发节点具有深队列缓存,那么CUBIC是可以完爆BBR的,这也是近期快放假时,一帮朋友帮我测试出来的结论。下图可以帮你理解这个过程,但欲知更多细节,还请卖洗衣阿拉膏儿诶:
也许你会想到延长BBR的带宽窗口的宽度,从10个采样周期延长到20个,延长最小RTT的维持周期,从10秒延长到20秒...但是这会增加丢包的概率,不幸的是,BBR对丢包又没有太大的反应,这必然会导致重传率的增加,进而变成以流量换速度这种中国式的垃圾方案。
在不考虑公平性的前提下,正确的抢占式优化方式将是,加宽两个窗口,并且对丢包进行反应。更加合理的方案不是伤筋动骨修改BBR代码的优化方案,而是参数调优方案。千万不要觉得BBR的所谓10个采样周期以及10秒中的最小RTT维持周期是拍脑袋拍出的啊。这些数字其实是算出来的,如果我们知道一条链路的物理带宽,固有RTT以及缓存队列的大小,pps,理论上即应该可以算出以上窗口的宽度。
几乎看不到有人能正确计算上述几个元素之间的关系,大多数人倾向于定性的分析,期待在感官上快速获得满足,这种心理是堕落的心理!再次提醒,不要犯罪,罪必罚!
BBR是完全基于Pacing Rate的吗?窗口的意义何在呢?
如文档所述,BBR是基于Pacing Rate的,窗口在BBR算法中不再是一个类似Reno/CUBIC那样无条件向上探测的值,它根据BBR采集到的实时带宽计算得到,表示最大inflight即BDP的容量。由于数据接收端会受到Delay ACK等影响,所以BBR会在计算拥塞窗口cwnd的时候,为其乘以一个200%的系数,以抵消Delay ACK带来的负面影响。我们可以看得出区别:
BBR之前--依靠拥塞算法加性增窗,然后用这个窗口计算Pacing Rate,窗口控制了一切。
BBR以及之后--正好相反,先采集并调整Pacing Rate,再通过这个Pacing Rate和最小RTT计算窗口。
因此,BBR的窗口是相对精确”测量“出来的,而不是通过算法”计算“出来的。
对于BBR而言,窗口的意义仅仅是限制网络中存在的最大数据量,而无法控制数据发送的Pacing Rate了。
Google的框架是个坑吗?
与一位大神交流,彼认为Google埋了一个大坑,tcp_congestion_ops的cong_control只为其自家使用。我对此仅仅部分认同这位大神的结论,但是我依然觉得这个框架是对Linux拥塞控制模块实现的一种解放。我觉得在经过孙中山所说的训政时期之后,才可以实施宪政,不用BBR的新框架,不用cong_control回调,能写出个自觉收敛而保障公平性的算法,而不是靠框架的惩罚获得收敛的算法,我觉得这就是所谓的“TCP训政”,之后公平性理念深入你心的时候,才可以用好好地用BBR框架写一个好好的算法。但是在此前,BBR也许来的有点早了。
在Linux 4.9内核的TCP拥塞控制框架之前(即在引入cong_control回调接管所有控制逻辑之前),一个拥塞控制算法的实现者不大可能闹出比较大的动静,因为罪与罚是分开的!你可以在你的cong_avoid里面犯罪,但是必然会在tcp_fastretrans_alert之后的tcp_cwnd_reduction中接受惩罚,后面这两者不是拥塞模块的实现者所能控制的!这比较符合社会规则,法庭必须是独立的且不受任何人控制的。由于法庭的存在,虽然缺少了完全自由发挥的空间,但毕竟公平性是可以保障的。
BBR之后,出现了cong_control回调,这个回调接管了TCP拥塞控制“法庭”的工作,自由度获得了大大的提升,但是也使得犯罪不必付出代价,这个回调函数会助长更多的罪!比如说,你完全可以在cong_control回调中直接给snd_cwnd设置一个非常大的常数窗口,比方说是1个亿,并且死活不降窗,Linux的TCP核心逻辑不会对你怎么着,你真的会发出1个亿的数据包,然后可能被丢掉9500万个包,接下来会陷入无穷尽的重传,最终连接崩溃!这绝对是一种损人不利己的做法!
如此看来,传统Linux TCP实现的做法是对的,BBR引入的cong_control是错误的!想想看,Google也挺能折腾的,用PRR替换掉了Rate Halving,如今有把自家的PRR给废了。
难道Google的BBR给TCP带来的框架的改变真的是个坑吗?
如果你看完了全文,就会发现,我并没有说TCP协议不允许实现者去测量带宽,是Reno,CUBIC这种自己不去测量的,这难道要怪别人吗?!AppEX肯定测量了更多的量,微软肯定也测量了,只有Linux是个奇葩!即便是CDG算法,也没有实现完整,仅仅是因为Linux的TCP拥塞控制算法实现方面掣肘太多?那为何CDG的作者不去像BBR的作者那般修改框架呢?我认为CDG,CUBIC这种如此傻的算法能能如此优秀,它们如果加那么一点点“手段”,一定会更好!那为什么它们没有触动Linux的TCP拥塞控制框架?!其实,这些算法的作者可能没有那个面子,Linux内核的TCP实现几乎被Google绑架了。
很多的算法和特性都是Google提出的,这一点正如20年以前Sun公司之于操作系统一样。Google的想法哪怕是脬屎也会如期进入内核,而其它的东西便再无这么幸运。既然Google是Linux内核社区网络领域的老户,那么它能引领什么呢?Google吐哺,天下归心!我希望Google继续怀揣使命感,而不要沦为像朗科,AppEX,网宿,BAT这种公司(我的意思是说,就算这些公司搞出了跟BBR一样的框架,甚至更容易犯罪的框架,他们也不会告诉你的,或者他们良心上的安慰就是:为了不让更多的人犯罪)。
我不知道微软系统的TCP到底是怎么实现的,但是我觉得一定不是Linux传统的这种实现,至少它不怕随便一个人可以编译一个设置窗口为1个亿的内核模块(你写过NDIS驱动吗?试试看?)。
如果说微软的掣肘之力在于其闭源,不开放的特征的话,Linux的掣肘之力便是相反的方面,即一切太自由了,正因为太自由,才要求玩家的水准更加高明,这种要求下,与其放纵,不如限制!cong_avoid可以胡写乱写,你可以写入一个仅有一行的语句,将窗口设置成一个亿而不会有什么后果,因为丢包后的PRR马上拉你回到现实,然而cong_control却不能胡写,不会再有任何机制提醒你够了(幸好没有将rwnd的控制也接管...)。这个话题继续扯下去,我可以扯完整个除夕,包括美国枪支问题,伊斯兰原教旨主义,中国城管的意义,最终会上纲上线一本正经地宣讲罗马共和国...
不管怎样,你有驾驭cong_control的水准吗?我更多地指的是在良心上。反正我暂时还没有,不过,我本善良。
后记-聊聊互联网和微信
连续八年的春节假期都是外出旅游,不回老家,不看春晚,不走亲戚,不聚会,不打牌,不为想做以上五件事找任何理由,还是很爽的,遗憾的是,没有同行者,因为大家都会忙着做以上几件事。
出发去旅游前,能写下这篇文章也觉得怪充实的,虽然还没有写完。不过既然是科普性软文,那我就在最后聊一下我并不十分感冒的互联网和微信(显然,不可否认,包括我都已经离不开它们了)。
从上海换到深圳已经一年3个月了,到了新的地方完全是换了思路,换了活法,之前我其实是很容易适应新环境的,这次却时常而不是偶尔会想起上海的朋友们,几个天天骂战的同事,几个一起搞VPN的奋斗者,一到周末就自驾周边游玩的老邻居,还有几个线下技术交流群...虽然我们这些人依然在微信群里保持的几乎每天都有的联系,但正是因为这样,我才觉得所谓”连接一切“是名不副实的。真正能连接一切的,不是靠互联网或者各种VR,而是物理上近距离接触。也许是我自己变老了,但矛盾的是,我放弃跟眼前的深圳这帮同事,朋友近距离物理接触,却在用微信和远在上海,北京,东北,中原地区的人交流,微信确实成功了。
大家聚餐的时候,一个个手里拿着手机刷朋友圈,群聊...你左边右边对面坐着的是你现在可以吹水,可以殴打的大活人啊,而你的微信里那些人与你的距离至少也是以公里计吧,远则以数千公里计!
微信的意义是,将彼此的联络推迟到你我分开以后。那么当你们在一起的时候,就别用微信了。过年大家都回家好好陪陪父母,跟同学老友近距离聊个天喝个酒打个架,等一个礼拜后你们分开了再聊微信吧。放下手机,即可肆意!
听了老罗的跨年演讲,我也想了很多,只是我没老罗那口才和技艺啊。
手机应用占掉了你一天的多少时间?老丈人说人活着就是为了这张嘴,我并不认同,因为现在不管是为了果腹还是为了享受,花在吃上的时间,精力,金钱都非常有限,现在再也不会一顿饭花掉你一个月工资了,也不会再有吃个饭从中午吃到午夜的了。现在生命中最重要的是什么?我觉得就是时间。手机占据你的时间,那就是在摧残你的生命,你的生命会因此而变短。人们可能会说No,如果一个从不用手机的人A在40岁死去,那些同龄的用手机的人难免唏嘘,毕竟A死的时候,大家还都活着,可是,这些人活着又能怎样呢?他们会线下联络吗?他们除了手机还有别的吗?这些人活着的意义是什么?难道就是把大米,粮油,蔬菜等资源消耗在低头看手机上吗?
过年了,让不过年的人感动地想哭,人间的感动难道不是离别太久后的重逢吗?
在军事史上,汉尼拔翻越阿尔卑斯山和毛泽东爬雪山过草地是比较著名的两次长征,如今,那些徒步回家过年,骑摩托车回家过年的人组成的新长征队伍,一点也不比汉尼拔和毛主席差。意义是什么?他们这么做是为了什么?是为了重逢!这是微信,微博,支付宝,QQ所解决不了的,谁能解决?铁道部能解决!过年时,铁道部完爆BAT全家!请继续相信中国铁路。
那么互联网是什么?
尼古丁通过人的嘴鼻进而通过肺部进入人体,酒精通过人的消化系统进入人体,毒品通过嘴鼻,血管注射进入人体,赌博通过人的欲望进入人体,这些让人上瘾的东西无一例外都是让你的大脑产生错觉,我并不完全否认这种错觉,毕竟李白斗酒诗百篇留下了很多的必须要背诵的诗,正是这些诗让我得以装逼显得很有文化。但是恐怖的是,互联网全面侵袭人的大脑,直接通过人的眼睛,耳朵进入人体的大脑并瞬间产生错觉,根本就不需要经过呼吸,消化,血液循环的漫长过程!细思极恐!你仔细想想,是不是有长辈把”吃XX会致癌“的文章转给你,然后你马上就谈XX色变了,瞬间改变了你的价值观,一篇鸡汤暖文就可以让你瞬间觉得你可以成为世界第一等,然后你会自愿的透支你的时间和精力去企图匹配你那完全不达标的能力,这些都是错觉!更严重的错觉是,你读着微信里别人发送的快乐文字,却看不到对方的表情和对方所处的场景,身边的人希望和你喝个愉快的下午茶,你的心却系在远方的微信好友身上,然而对方却根本就不在乎你,因为他也有远方的微信好友。这全都是错觉。
BBR试图做且正在做的事情是,让这一切针对大脑的错觉注入,发生的更快一些!
皮鞋!
文章评论