BBR学习笔记

本文详细介绍了基于丢包的拥塞控制算法的局限性,特别是BufferBloat问题,然后重点阐述了Google的BBR算法如何通过控制RTT和BottleneckBandwidth来优化网络性能,包括其工作原理、阶段转换和源码分析。
摘要由CSDN通过智能技术生成

1.拥塞控制简介

当今互联网上主要使用的拥塞控制算法(Congestion Control Algorithms)都是基于****丢包的拥塞控制(loss-based congestion control)**,他的基本运行方式可以分为以下几个阶段:

  1. 刚开始建立连接时进入 slow start 阶段,指数级别地成倍增加发送数据的速率,试探网络的带宽上限
  2. 增长到达一定限度 ssthresh 之后,进入线性增长 Congestion Avoidance的阶段,慢慢增加发送的速率
  3. 直到检测到 丢包(loss), 认为网络发生了拥塞,大幅度减少滑动窗口的大小
  4. 一段时间后,重新回到 slow start 的状态

请添加图片描述

​ 基于丢包的拥塞控制算法

这套算法是20世纪80年代设计的,后来随着网络环境的变化,也逐步推出了TCP Tahoe, Vegas, Reno, **Cubic(现在Linux系统的默认算法)**等改进版本的算法,在原有基础上增加了一些更细致的功能,但整体思路不变,依旧是基于丢包来进行拥塞控制。

1.1 丢包算法的缺点

丢包≠拥塞(loss ≠ congestion)

过去的网络环境往往带宽较小,而且以有线线路居多,所以一旦发生丢包的行为,99%是由于拥塞导致的,所以CUBIC这样的算法才有效地支撑了互联网几十年来的发展。

  • 但是当今的网络环境,链路带宽高达数十Gbps,是当年的六个数量级以上,在相同出错率的情况,丢包的发生情况就会变多。

  • 网络交换节点的buffer变大

  • -随着高速buffer的价格越来越低,网络交换节点开始使用大容量的buffer来应对更大的网络需求。但是正如之前所说,基于丢包的算法在初始阶段会指数级增加发送的数据,就是想要把buffer填满,填满之后,再发送数据给交换机就会产生丢包,从而控制当前的连接速率下降

在这里插入图片描述
BufferBloat问题

  • 但是如果Buffer很大,这样的策略就会导致BufferBloat问题,大量的数据卡在交换节点的buffer中,排着队等待被发送出去,导致**排队延迟(Propagation Delay)**增大,从而使得整体的网络延迟增加。

2. BBR改善

2.1 一些概念

2.1.1 瓶颈链路

首先我们来解决第一个问题,对于一个全双工的TCP连接来说,在任意时刻,它在每个方向都有且只有一段最慢的链路(exactly one slowest link)或称瓶颈(bottleneck)

请添加图片描述

瓶颈链路(Bottleneck Link),管道最细处决定了最大速率,同时也是数据堆积的地方
瓶颈的概念很重要,因为:

  • 瓶颈决定了连接的最大数据传输速率,就好像一根水管最细处决定了它整体的水流量。

  • 瓶颈也是持久队列(persist queues)形成的地方。

  • 对于瓶颈链路,只有当它的离开速率(departure rate)大于到达速率(arrival rate)时,这个队列才会收缩,否则就会不断堆积,而一条链路上其他的队列也会朝着瓶颈移动(因为它的传输速率最小)。

不管一条连接会经过多少条链路,以及每条链路的速度是多少,从 TCP 的角度来看, 任何一条复杂路径的行为,与 RTT(round-trip time)及瓶颈速率相同的单条链路的行为是一样的。 换句话说,以下两个物理特性决定了传输的性能

  • RTprop (round-trip propagation time):往返传输时间(假如没有排队的情况下, 简称为 RTT

  • BtlBw (bottleneck bandwidth):瓶颈带宽

2.1.2 带宽延迟积

带宽延迟积(Bandwidth Delay Product) BDP , 顾名思义就是将RTT和瓶颈带宽相乘得到的概念,如果将网络路径视为一条管道,那么RTT就是管道的长度,Bandwidth是管道的直径,而BDP表示的则是管道的容量大小。所以对应到网络中, BDP 表示的意思就是某一时刻的网络中仍在传输中的最大数据量(buffer中留存的数据除外)。

在这里插入图片描述

2.1.3 inflight

是指那些已经发送出去,但是还没有得到确认的数据,也就是前面提及的,仍在管道中的数据。某一时刻的inflight数据量(也就是正在传输中的数据)决定了当前的网络的吞吐量(Throughput) 和 RTT。

在这里插入图片描述

  • BDP之前,瓶颈链路还没有到达瓶颈,吞吐量可以在不断上升,此时RTT保持不变。

  • BDP点,瓶颈链路刚好到达瓶颈,如果发送端继续发送数据包,数据包将会在网络节点中排队,发生buffer bloat问题,排队时延增加,导致RTT增加。

直观上来说, 拥塞(congestion)就是 inflight 数据量持续向右侧偏离 BDP 线的行为, 而拥塞控制(congestion control)就是各种在平均程度上控制这种偏离程度的方案或算法。

最佳的策略就是将inflight data控制在上图红色圈圈的位置,这样我们就能够实现当前网络下最大的Bandwidth和最小的RTT。很不幸的是,在1981年,Gail & Kleinrock 就已经证明了理论上不可能设计出一个算法可以找到这个点,于是退而求其次,大家选择了基于丢包的拥塞控制算法,在当年,缓冲区域的Buffer只比BDP略大,所以导致的额外延迟很小,红点和蓝点之间距离很小,但是现在缓冲区已经比BDP大上好几个数量级了bufferbloat导致的RTT达到了秒级,这就没有办法忽视了。

在这里插入图片描述

而BBR算法的思路简单来说就是,努力将inflight数据量控制在最优点右侧一点点,努力达到转折点的位置,从而获得更小的RTT和更大的吞吐量,而实现的方式就是通过Bottlenect Bandwidth 和RTT。

3. BBR控制原理

3.1 RTT与Bottleneck Bandwidth控制

当一个连接满足以下两个条件时,它将运行在最高吞吐和最低延迟状态(也就是上一节中提到的红色最优点):

  1. 速率平衡(rate balance):瓶颈链路的数据包到达速率刚好等于瓶颈带宽 BtlBw

    • 这个条件保证了链路瓶颈已达到 100% 利用率。
  2. 管道填满(full pipe):传输中的总数据(inflight)等于 BDP(= BtlBw × RTprop)。

    • 这个条件保证了有恰好足够的数据,既不会产生瓶颈饥饿(bottleneck starvation), 又不会产生管道溢出(overfill the pipe)
  • BBR通过控制数据发送的速率(pacing rate) 来保证速率平衡

  • 同时通过控制拥塞滑动窗口(cwnd)的大小来控制inflight数据量等于BDP

3.2 BBR参数

为了让数据包的到达速率(packet-arrival rate)能匹配到 瓶颈链路的离开速率(departure rate), BBR 会对每个数据进行 pace (在每个 RTT 窗口内均匀发送数据)。

总的来说BBR有两个控制参数:

  1. pacing_gain:BBR 的主要控制参数,控制发送的速率

  2. 要求达到速率必须匹配到瓶颈速率,意味着 pacing 是 BBR 设计中 必不可少的部分

  3. cwnd_gain:拥塞滑动窗口的大小 cwnd等于 BDP 乘以一个略大于 1 的系数 cwnd_gain, 用来控制当前网络中的inflight数据量。略大于1是为了容忍常见的网络和接收端异常(pathologies),这个我们后面会讲到。

3.3 BBR的四个不同阶段

3.3.1 StartUp

StartUp 阶段,类似传统TCP阶段的slow start 阶段,会成倍的增长发送速率和cwnd,试探当前网络的性能瓶颈

StartUp 阶段类似于CUBIC算法的slow start阶段,发生在连接刚刚建立的时候, 会指数级增长 cwnd pacing rate 快速地把水管注满,探测到当前网络环境的天花板,记录下此时的 RTTBW

在这一阶段 pacing_gaincwnd_gain 的值都为 2/ln2 , 大约是2.885, 具体为什么是这个值呢?因为网络协议一个重要的指标是公平性,如果在start up阶段增长的速度太快,就会挤占别人的带宽资源,这样不太好,如果你增长速度太慢自身性能当然也不太行。

所以最好的方式就是和别的算法差不多,而这里所谓别的算法其实也没有别人,正是CUBIC为首的Loss-based算法。CUBIC的slow start速率是每过一个RTT,cwnd就会增长一倍,CUBIC使用它自己设置一套模型来实现这个目标,而BBR就需要在自己的模型下也模拟出这样一个每个RTT都会成倍增长的效果。将 pacing_gain cwnd_gain 设置为 ,就可以近似模拟出这样一条曲线。

此外,这个阶段也会记录下探测到的最小RTT和最大带宽,这在后面 ProbeBW 阶段会非常有用。

3.3.2 Drain

Drain阶段当检测到RTT不断增加,而BW基本不变之后,说明我们到达了BDP最优点右侧的部分,产生了队列堆积,算法就会进入 Drain 阶段,快速降低发送速率,将inflight数据量排空至BDP大小

Startup 阶段难免会用力过猛,把水管注满,甚至把填充了许多buffer的空间,导致出现了堆积队列,如果BBR发现最近一段时间 RTT 开始增加,而 BW 基本没有增长,就意识到已经到达了瓶颈,可以进入 Drain 阶段了。

所以 DRAIN 阶段的目标就是把 StartUp **阶段中注入过多的inflight数据排空,**使得 inflight=BDP , 这样延迟才会降低,同时吞吐量也足够大。

此时的 pacing_gain 会设置为 , 也就是 StartUp 阶段的倒数,指数级降低当前发送速率,直到inflight 数据量等于bdp.

另外,此时的 cwnd_gain 其实仍然是 ,虽然看上去很大,但 cwnd 会维持不变,因为 cwnd=cwnd_gain * BtlBW * RTT ,后面两个变量在进入这一阶段后都不会改变,所以cwnd也不会有任何变化。

3.3.3 PROBEBW

PROBEBW阶段在inflight=BDP之后, 就会进入平稳的 PROBEBW 阶段,这也是绝大部分时间所处的阶段。此时会将发送速率控制在一个稳定的范围内,偶尔会增加一下发送速率,看看网络环境是否有变化(带宽是否增加)。

这一阶段基本不会对发送速率做出过多的改变,但是也会不断试探网络变化,有没有更高的带宽可以使用,所以简单来说要完成以下目标:

  • 维持发送速率基本控制在 BtlBW 左右,充分利用瓶颈带宽。

  • 维持 inflight = BDP ,保证网络的延迟稳定。

  • 周期性地试探更高的带宽上限,试探本身可能会导致数据堆积,所以试探结束后会降低发送速率,把队列排空。

在这里插入图片描述

通过设置 周期性地改变 pacing_gain 的值来实现,以8个RTT为一个循环,探测带宽的阶段就设置为1.25, 探测结束后就设置0.75排空网络中堆积的数据,然后剩下六个RTT就会使用1.0来平稳地传输数

3.3.4 ProbeRTT

ProbeRTT阶段为了适应网络环境可能的剧烈变化,每隔10s左右,还会进入 ProbeRTT阶段,将cwnd设为一个极小的值,排空当前的inflight数据量,保证这段时间内的RTT为理论最小值。

周期性地进入这个阶段来测量最小的RTT,也就是 min_rtt 的值,来适应网络环境的变化。

具体的实现方案如下,每隔10s时间,会从 Probe_BW 状态进入 Probe_RTT 状态,此时会将 cwnd **设置为4(**保证至少一条数据可以合理传输),持续时间为 max(rtt, 200ms) 。由于基本不传输新的数据,这段时间内基本会把inflight数据排空,保证管道的通畅,这样我们也就能够测到最真实的 min_rtt 值。

3.3.5 状态机切换

在这里插入图片描述

4.BBRV1源码

4.1 伪代码

4.1.1 收到ack时
function onAck(packet)
  rtt = now - packet.sendtime                      // 收包时间 减去 包中记录的发包时间就是RTT
  update_min_filter(RTpropFilter, rtt)             // 更新对 RTT 的估计
 
  delivered      += packet.size
  delivered_time =  now
  //计算当前实际的传输速率
  delivery_rate  =  (delivered - packet.delivered) / (delivered_time - packet.delivered_time)
 
  if (delivery_rate > BtlBwFilter.current_max      // 实际传输速率已经大于当前估计的瓶颈带宽,或
     || !packet.app_limited)                       // 不是应用受限(应用受限的样本对估计 BtlBw 无意义)
     update_max_filter(BtlBwFilter, delivery_rate) // 根更新对 BtlBw 的估计
 
  if (app_limited_until > 0)                       // 达到瓶颈带宽前,仍然可发送的字节数
     app_limited_until = app_limited_until - packet.size
4.1.2 发送数据包时
function send(packet)
  bdp = BtlBwFilter.current_max * RTpropFilter.current_min  // 计算 BDP
  if (inflight >= cwnd_gain * bdp)                          // 如果正在传输中的数据量超过了允许的最大值
     return                                                 // 直接返回,接下来就等下一个 ACK,或者等超时重传

  // 能执行到这说明 inflight < cwnd_gain * bdp,即正在传输中的数据量 < 瓶颈容量
  if (now >= next_send_time)
     packet = nextPacketToSend()
     if (!packet)                      // 如果没有数据要发送
        app_limited_until = inflight   // 更新 “在达到瓶颈容量之前,仍然可发送的数据量”
        return

     packet.app_limited = (app_limited_until > 0)  // 如果仍然能发送若干字节才会达到瓶颈容量,说明处于 app_limited 状态
     packet.sendtime = now
     packet.delivered = delivered
     packet.delivered_time = delivered_time
     ship(packet)
     //下次发送数据的时间,通过这个控制拥塞
     next_send_time = now + packet.size / (pacing_gain * BtlBwFilter.current_max)
 //用定时器设置下次发送时间到期后的回调函数,就是继续执行send函数
  timerCallbackAt(send, next_send_time)

4.2 算法流程

/net/ipv4/tcp_bbr.c

在这里插入图片描述

5 参考链接

bbr原理解读

bbr源码分析

bbr状态切换

bbrv3源码

bbr论文

  • 31
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值