周六,由于要赶一个月底的Deadline,因此选择了在家VPN加班,大半夜就爬起来跑用例,抓数据...自然也就没有时间写文章和外出耍了...不过利用周日的午夜时间(不要问我为什么可以连续24小时不睡觉,因为我觉得吃饭睡觉是负担),我决定把工作上的事情先放下,还是要把每周至少一文补上,这已经成了习惯。由于上周实在太忙乱,所以自然根本没有更多的时间去思考一些“与工作无关且深入”的东西,我指的与工作无关并非意味着与IT,与互联网无关,只是意味着不是目前我在做的。比如在两年前,VPN,PKI这些是与工作有关的,而现在就成了与工作无关的,古代希腊罗马史一直都是与工作无关的,直到我进了罗马历史研究相关的领域去领薪资,直白点说,老板不为之给我支付薪水的,都算是工作无关的东西。玩转与思考这些东西是比较放得开的,不需要对谁负责,没有压力,没有KPI,没有Deadline,完全自由的心态对待之,说不定真的很容易获得真知。
       我认识一个草根鼓手朋友,玩转爵士鼓的水准远高于那些所谓的专业鼓手,自然带有一种侠客之风传道授业解惑,鼓槌随心所欲地挥舞在他自己的心中,没有任何负担和障碍,任何的节奏都可以一气呵成,从来不打重复鼓点,那叫一个帅!然而他并非专业考级出来的,是拜师出来后自己摸索的。
       要兴趣去自然挥洒,而不是迫于压力去应对。
       我也是鼓手,但我打的不是爵士鼓,我是鼓噪者,技术的鼓噪者。本文与TCP BBR算法相关。

0.说明

BBR热了一段时间后终于回归了理性,这显然要比过热地炒作要好很多。这显然也是我所期望的。
       本文的内容主要解释一些关于BBR的细节问题。这些问题一般人可能不会关注,但是针对这些问题仔细思考的话,会得到很多有用的东西。在解释这些问题时,我依然倾向于使用图解的方式,但这一次我不再使用Wireshark的tcptrace图了,而是使用时序图的方式,因为这种时序图既然能够令人一目了然地解释TCP三次握手,四次分手,TIME-WAIT等,那它自然也能解释更复杂的机制,比如说拥塞控制。

1.延迟ACK以及ACK丢失并不会影响TCP的传输速率

在大的时间尺度上看,延迟ACK以及ACK丢失并不会对速率造成任何影响,比如一个文件4个TCP段正好发完,即便前面几个ACK全部丢失,只有最后一个到达,那它的传输总时间也是不变的。
       但是在细微的时间段内,由于延迟ACK或者ACK丢失带来的时间偏差却是不可忽略的。
       首先我们再次看一下BBR是如何测量即时速率的。测量即时速率需要做一个除法,分子是一段时间内成功到达对端的数据包总量,分母就是这段时间。BBR会在每收到一次ACK的时候测量一次即时速率。计算需要的数据分别在数据传输和数据被ACK的时候采样。很显然,我们可以想当然地拍脑袋得出一个算法:
设数据包x发出的时间为t1,数据包x被应答的时间为t2,则在数据包x被应答时采集的即时速率为:
Rate=(从x被发出到x被应答之间一共ACK以及SACK了多少个数据包)/(t2-t1)

但是这会造成什么问题呢?这会造成误差。如下图所示:

BBR如果依赖这种即时的速率测量机制来运作的话,在ACK丢失或者延迟ACK的情况下会造成测量值偏高。举一个简单的例子:

那么,BBR是如何做到不引入这种误差从而精确测量即时速率的呢?很简单,将t1改成至数据包x发出时为止,最后一个(S)ACK收到的时间即可。
       详情请参考内核源码的net/ipv4/tcp_rate.c文件,原理非常简单。
       所以说,BBR的速率测量值并不受延迟ACK,ACK丢失的影响,其测量方法是妥当的。之所以上面给出一个错误的方法,是想展示一下什么样的做法是不妥当的,以及容易引起质疑的点在哪里。
       结论很明确,延迟ACK,ACK丢失,并不影响BBR速率的采集值。
       接下来谈第二个问题,关于BBR的拥塞窗口大小的问题。

2.为什么BBR要把计算出来的BDP乘以2作为拥塞窗口值?

这个问题可以换一种问法,即BBR的bbr_cwnd_gain值如何解释:

/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */
static const int bbr_cwnd_gain  = BBR_UNIT * 2;

我们知道,BBR将Pacing Rate作为第一控制要素,按照计算得到的Pacing Rate平缓地发送数据包即可,既然是这样,拥塞窗口的存在还有何意义呢?
       BBR的拥塞窗口控制已经退化到了规定一个限额,它主要是为了灌满管道,解决由于ACK丢失导致的无包可发的问题。
       我先来阐述问题。
       BBR第一次把速率控制计算和实际的传输相分离,又一个典型的控制面与数据面相分离的案例。也就是说,BBR核心模块计算出一个速率,然后就把数据包扔给Pacing发送引擎模块(当前的实现是FQ,我自己也实现了一个),具体何时发送由Pacing发送引擎来控制,二者之间通过一个发送缓冲区来交互,具体结构如下图:

可见,拥塞窗口控制的是“到底扔多少数据到发送缓冲区合适”的。接下来的问题显然就是,拥塞窗口到底是多少合适呢?
       虽然BBR分离了控制逻辑和数据发送逻辑,但是TCP的一切都是ACK时钟驱动的,如果ACK该来的时候没有来,比如说丢了,比如延迟了,那么就会影响BBR整个核心的运作,进而影响Pacing发送引擎的数据发送动作,BBR要做的是,即便没有ACK来驱动,也可以自行发送本该发送的数据包,因此Pacing发送引擎的发送缓冲区的意义重要,说白了就是,发送缓冲区里一定要有足够的数据包才行,就算ACK没有来,引擎还是有包可发的。
       下面来展示一幅图:

如果这个图有不解之处,像往常一样,大家一起讨论,但总的来讲,我觉得问题不大,所以说才会基于上图产生了下图:

该图示中,我把TCP层的BBR核心模块和FQ的发送模块都画了出来,这样我们可以清晰看出拥塞窗口的作用。实际上,BBR核心模块按照拥塞窗口即inflight的限制,将N个数据包注入到Pacing发送引擎的发送缓冲区中,这些包会在这个缓冲区内部排队,最终在轮到自己的时候被发送出去。由于这个缓冲区里有足够的数据包,所以即使是ACK丢失了多个,或者接收端有LRO导致ACK被大面积聚集且延迟,发送缓冲区里面的数据包也足够发送一阵子了。
       维护这么一个发送缓冲区的好处是在缓冲区不溢出(为什么不溢出?那是算出来的,正好两倍)的前提下时时刻刻有包可发,然而代价也是有的,就是增加了RTT,因为在发送缓冲区里排队的时间也要被算在RTT里面的。不过这无所谓,这并不影响性能,数据包不管是在TCP层的发送队列里,还是在FQ的队列里,最终都是要发出去的。值得注意的是,本地的FQ队列和中间节点的队列性质完全不同,本地的队列是独占的,主动的,而中间节点队列是共享的,被动的,所以这里并没有因为RTT的增加而损失性能。这就好比你有一张银行卡专门用来还房贷,由于利息的浮动,所有每月还款金额不同,为了不欠款,你每个月总是要存进足额的钱进去,一般要远多于平均的还贷额度才最保险,但这并不意味着你多存了钱这些钱就亏了,在还清贷款之前,存进去的钱早晚都是要还贷的。

3.为什么在探测最小RTT的时候最少要保持4个数据包

首先要注意的是,用1个包去探测最小RTT会更好,然而效率可能会更低;用5个包去探测最小RTT效率更好,但是可能会导致排队,为什么4个包不多也不少呢?
       我尝试用一个时序图来说明问题:

可见,4个包的窗口是合理的,infilght分别是:刚发出的包,已经到达接收端等待延迟应答的包,马上到达的应答了2个包的ACK。一共4个,只有1个在链路上,另外1个在对端主机里,另外2个在ACK里。路上只有1个包,这绝对合理,如果一条路连1个包都容纳不下了,那还玩个屎啊!
       以上的论述,仅仅为了帮大家理解以下一段注释的深意:

/* Try to keep at least this many packets in flight, if things go smoothly. For* smooth functioning, a sliding window protocol ACKing every other packet* needs at least 4 packets in flight:*/
static const u32 bbr_cwnd_min_target = 4;

4.用时序图总览一下BBR的Startup/Drain/ProbeBW阶段

我以下面的时序图展示一下BBR的流程:

5.Startup阶段拥塞窗口计算的滞后性

我们知道,BBR里面拥塞窗口已经不再是主控因素,事实上它的名字应该改成“发送缓冲区限额”会比较合适了,为了方便起见,我仍然称它为拥塞窗口,虽然它的含义已经改变。
       在Startup阶段,发送速率每收到一个ACK都会提高bbr_high_gain:

/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain* that will allow a smoothly increasing pacing rate that will double each RTT* and send the same number of packets per RTT that an un-paced, slow-starting* Reno or CUBIC flow would:*/
static const int bbr_high_gain  = BBR_UNIT * 2885 / 1000 + 1;

这个其实跟传统拥塞算法的“慢启动”效果是类似的。
       然而BBR计算拥塞窗口是用“当前采集到的速率”乘以“当前采集到的最小RTT”来计算的,这就造成了“当前发送窗口”和“当前已经提高的速率”之间的不匹配,所以,计算拥塞窗口的时候,gain因子也必须是bbr_high_gain,从而可以吸收掉速率的实际提升。

6.由ACK通告的接收窗口还有意义吗?

在以往的Reno/CUBIC年代,窗口的计算是根据既有的固定数学公式算出来的,完全仅仅由ACK来驱动,无视事实上的传输速率,所以彼一时的拥塞窗口仅仅可以代表网络的情况,即便如此,这种网络状态的结论也是猜的。
       到了BBR时代,主动测量传输速率,将网络处理能力和主机处理能力合二为一,如果网络瓶颈带宽为10,而主机处理能力为8,那么显然采集到的带宽不会大于8!反之亦然。如果BBR测量的即时速率很准确的话,我想通告窗口就完全没有意义了,通告的接收窗口会被忠实地反映在发送端采集到的即时速率里。BBR只是重构了拥塞控制算法,但还没有重构TCP处理核心,我想BBR可以重构之!

7.BBR在计算拥塞窗口时其它的关键点

1>.延迟ACK的影响

计算拥塞窗口的时候,会将目标拥塞窗口进行一下调整:

/* Reduce delayed ACKs by rounding up cwnd to the next even number. */
cwnd = (cwnd + 1) & ~1U;

此处向上取偶数就是为了平滑最后一个延迟ACK的影响,如果最后一个延迟ACK该来的没来,那么这个向上取偶数可以为之补上。

2>.Offload的影响

 * To achieve full performance in high-speed paths, we budget enough cwnd to* fit full-sized skbs in-flight on both end hosts to fully utilize the path:*   - one skb in sending host Qdisc,*   - one skb in sending host TSO/GSO engine*   - one skb being received by receiver host LRO/GRO/delayed-ACK engine
.../* Allow enough full-sized skbs in flight to utilize end systems. */cwnd += 3 * bbr->tso_segs_goal;

...

8.关于我的Pacing发送引擎

我在今年1月份写了一版和TCP BBR相结合的Pacing发送引擎,以消除FQ对RTT测量值(增加排队延迟)的影响,详见:
《 彻底实现Linux TCP的Pacing发送逻辑-普通timer版》
《 彻底实现Linux TCP的Pacing发送逻辑-高精度hrtimer版》
个人觉得我这个要比FQ那个好很多,毕竟是原汤化原食的做法吧。
       直接在TCP层做Pacing其实并不那么Cheap,因为三十多年来,TCP并没有特别严重的Buffer bloat问题,所以TCP的核心框架实现几乎都是突发数据包的,完全靠ACK来驱动发送,这个TCP核心框架比较类似一个令牌桶,而不是一个整型器!
令牌桶:决定能不能发送;
整型器:决定如何发送数据,是突发还是Pacing发送;
可见这两者是完全不同的机制!要想把一个改成另一个,这个重构的工作量是可想而知。因此我实现的那个TCP Pacing只是一个简版。真正要做得好的话,势必要重构TCP发送队列的操作策略,比如出队,入队,调度策略。
       现阶段,我们能使用的一个稳定版本的Pacing替代方案就是FQ,我们看看Linux的注释怎么说:

/* Set the sk_pacing_rate to allow proper sizing of TSO packets.* Note: TCP stack does not yet implement pacing.* FQ packet scheduler can be used to implement cheap but effective* TCP pacing, to smooth the burst on large writes when packets* in flight is significantly lower than cwnd (or rwin)*/

结语

今天是周六,白天我折腾了一天工作,结果没有什么结果,也算认了。我又不能让这么一天就这么过去,于是我去超市买了一瓶真露,回到家看了个系列纪录片(关于甲午战争的),然后写完并补充了这篇文章,唉,一想到天亮我就倍感恐惧,老婆一天都要去代课,小小下午还有排练和培训,家里还有一大堆挂件安装工作...

使用TCP时序图解释BBR拥塞控制算法的几个细节相关推荐

  1. Google BBR拥塞控制算法背后的数学解释 | 深度

    参加 2019 Python开发者日,请扫码咨询 ↑↑↑ 作者 | 赵亚 转载自CSDN网站 杭州待了一段时间,回到深圳过国庆假期,无奈温州皮鞋?厂老板过节要回温州和上海,不在深圳,也就没有见着,非常 ...

  2. 「深度好文」TCP BBR拥塞控制算法深度解析

    linux服务器开发相关视频解析: tcpip,accept,11个状态,细枝末节的秘密,还有哪些你不知道 徒手实现网络协议栈,请准备好环境,一起来写代码 c/c++ linux服务器开发学习地址:c ...

  3. 来自Google的TCP BBR拥塞控制算法解析

    写本文的初衷一部分来自于工作,更多的来自于发现国内几乎还没有中文版的关于TCP bbr算法的文章,我想抢个沙发.本文写于2016/10/15!         本文的写作方式可能稍有不同,之前很多关于 ...

  4. Google BBR拥塞控制算法背后的数学解释

    杭州待了一段时间,回到深圳过国庆假期,无奈温州皮鞋?厂老板过节要回温州和上海,不在深圳,也就没有见着,非常遗憾! 国庆节当天,就写这个了.经理不会弹琴,但是经理会弹琴. 我原本可能会在想国庆节的凌晨到 ...

  5. Google的TCP BBR拥塞控制算法深度解析

    原作者:dog250,授权发布 重新整理:极客重生 hi ,大家好,今天推荐一篇我认为在TCP BBR技术里面分析非常透彻的文章,希望大家可以学习到一些真正的知识,理解其背后的设计原理,才能应对各种面 ...

  6. 个人感悟—来自Google的TCP BBR拥塞控制算法解析

    地址:TCR BBR拥塞控制算法另类解析 写本文的初衷一部分来自于工作,更多的来自于发现国内几乎还没有中文版的关于TCP bbr算法的文章,我想抢个沙发.本文写于2016/10/15! 本文的写作方式 ...

  7. Google BBR拥塞控制算法模型初探

    女主宣言 本文出自于ADDOPS团队,该文章是一篇兴趣触发的探索性文章,作者是一名刚毕业的小鲜肉,买新系统,去自己探索bbr算法,难能可贵,并且给出了详细的部署步骤,也能让大家在兴趣之余跟着步骤试一把 ...

  8. 一文解释清楚Google BBR拥塞控制算法原理

    BBR对TCP性能的提升是巨大的,它能更有效的使用当下网络环境,Youtube应用后在吞吐量上有平均4%提升(对于日本这样的网络环境有14%以上的提升): 报文的往返时延RTT降低了33%,这样如视频 ...

  9. Google's BBR拥塞控制算法如何对抗丢包

    我不知道该怎么说.总之,便舍船,从口入,我看不到黄发垂髫并怡然自乐!我不会说什么,除了咒骂!         在BBR之前,存在着两种拥塞控制算法,基于丢包的和基于时延的,不管哪一种都是基于探测的,换 ...

最新文章

  1. [IoC容器Unity]第一回:Unity预览
  2. (接口)让僵冷的翅膀飞起来---从实例谈OOP、工厂模式和重构
  3. 浮点数为何不能进行相等性比较
  4. netty:NIO模型--选择器(Selector)
  5. 2019,从刷新你的运营知识库开始!
  6. 怒卸python3.4.1
  7. while循环打印*菱形
  8. Service Worker,Web Worker,WebSocket的对比
  9. 水厂计算机知识,计算机信息在水厂中的管理与应用的探讨
  10. emlog过滤html,Emlog 搜索优化 标题 + 全文搜索
  11. 洛谷 P2495 [SDOI2011]消耗战
  12. android方法中添加 N,AndroidN(7.0)Settings模块界面加载流程学习
  13. java excel 转 图片_有什么方法可以用java 将word或者Excel文件转换成图片文件?
  14. 如何理解刑法中的“以非法占有为目的”
  15. mhw跳过结尾_怪物猎人世界怎么跳过剧情
  16. vs2015开发在网页中调用的ocx控件详解(二)
  17. 享元模式实例与解析实例二:共享网络设备(有外部状态)
  18. [附源码]java毕业设计基于的高校学生考勤管理系统
  19. Java 添加、读取、删除PPT文档属性
  20. mysql 常用日期时间计算

热门文章

  1. Goodwin模型参考自适应控制方案
  2. uva352 The Seasonal War-python
  3. 一次网络波动引发的事故!后端警醒!
  4. 中国国家地名信息库数据加密破解
  5. python的数据库中间件_sparrow
  6. i7和i5(学习资料)
  7. 315/433M 无线发射芯片 XL4456 功能和用途简单介绍
  8. word多张图片如何对齐
  9. c语言按键亮灯再按不亮,按一次按键灯亮,再按一次灯灭
  10. H5实现全屏与F11全屏