BBRv1的问题

  1. 带宽探测过于激进,导致丢包率过高。 BBRv1的ProbeBW阶段为了探测带宽,会周期性地使用1.25的增益系数发送数据。在网络缓冲队列很小(Shallow Buffer)的情况下,这额外25%的流量无法被吸收,会直接导致周期性的丢包
  2. 抢占性过强,对其他流不公平。 BBRv1主要根据自己测量的BtlBw来发送数据,不太会因为网络拥堵而主动大幅降低速率。当BBRv1与传统的基于丢包的算法(如CUBIC、Reno)共享一个瓶颈链路时,会发生以下情况:
    • BBRv1为了探测带宽,会持续填满(甚至溢出)队列。
    • CUBIC等算法检测到丢包或高延迟后,会立刻大幅降低自己的发送速率(进入拥塞避免阶段)。
    • BBRv1看到CUBIC让出了带宽,会认为网络瓶颈变大了,于是进一步增加自己的发送速率,抢占更多的带宽。
  3. 难以感知真正的拥塞。 BBRv1通过deliveryRate来估计带宽。但在某些复杂场景下,例如发生乱序(reordering)聚合确认(ACK aggregation)时,ACK返回的模式会欺骗BBRv1,使其在短时间内误以为交付速率非常高,从而高估了BtlBw。这会导致它以过高的速率发送数据,加剧网络拥塞。
  4. ProbeRTT阶段导致带宽利用率下降。 为了测量真实RTprop,BBRv1会进入ProbeRTT状态,将飞行数据量降至极低的4个数据包。这个过程持续至少200毫秒。在带宽延迟积(BDP)非常大的高速长距离网络中,4个数据包远不足以填满管道。这意味着每隔10秒,网络带宽就会有200毫秒的时间被闲置,在高带宽场景下这会造成可观的吞吐量损失。

BBRv1通过在收到每个ACK时测量交付速率来更新瓶颈带宽(BtlBw):DeliveryRate = 交付的字节数 / 交付这些字节所花费的时间

聚合确认导致虚假交付率

  1. 发送方以100Mbps的速率,发送了P1, …, P100 一共100个数据包。
  2. ACK被聚合: 接收方为了效率,将这100个包的ACK攒了起来,然后在一个非常短的时间窗口内(比如几毫秒内)连续发回给发送方。
  3. 发送方在T1时刻收到ACK-1,几微秒(μs)后在T2时刻收到ACK-2,又过了几微秒在T3时刻收到ACK-3…
  4. 当收到ACK-2时,BBRv1计算交付速率。
    • 交付的字节数 = 1个包的大小(例如1500字节)。
    • 交付时间 ≈ T2 - T1(几微秒)。
    • DeliveryRate = 1500 bytes / (几 μs)
  5. 这个计算结果会可能会得出高达几十Gbps甚至更高的瞬时交付速率。
  6. 这个巨大且错误的DeliveryRate样本进入了BBRv1的BtlBw滑动窗口最大值滤波器。由于它是窗口内的最大值,BtlBw会被立刻更新为这个虚假的高值。
  7. 在接下来的6-10个RTT周期内,它会用这个被污染的BtlBw来计算pacing_rate,从而以远超实际网络承受能力的速率疯狂发送数据,瞬间填满所有路由器缓冲区,造成严重的延迟飙升和大量丢包。

乱序导致虚假交付率

  1. 发送方依次发送了P1(序列号0-999), P2(1000-1999), P3(2000-2999)。
  2. 乱序到达: 接收方先收到了P1,然后是P3,P2因为网络路径问题被延迟了。
  3. 收到P1后,接收方回复ACK 1000。收到P3后,由于P2还没到,无法进行累积确认。它只能再次回复ACK 1000(这是一个重复ACK)。
  4. 过了一会儿,被延迟的P2到达。此时,接收方它立刻发送了一个新的累积确认ACK 3000。
  5. 发送方先是正常地收到了ACK 1000。紧接着,它可能很快就收到了ACK 3000。
  6. 当ACK 3000到达时,BBRv1发现,新确认的数据量是3000 - 1000 = 2000字节(P2和P3的数据)。
  7. 计算出DeliveryRate = 2000 bytes / (一个很短的ACK到达间隔)

BBRv2的改进

更快的启动阶段

BBRv2对启动阶段做了优化,使其在初始带宽探测时能更快地收敛,同时减少了在启动末期造成的排队溢出

拥塞感知的带宽探测

BBRv2的带宽探测不再是“盲目”地以1.25倍增益向前冲,而是变得小心翼翼。

  • 引入拥塞容忍度:BBRv2设定一个丢包率或ECN标记率的上限。在ProbeBW探测带宽时,如果发现丢包/ECN标记超个阈值,会立即停止增加发送速率,甚至会降低速率。
  • 更长的探测周期:BBRv2不再执着于在单个RTT内完成带宽探测,而是将探测过程拉长到多个RTT,以更温和的方式增加流量,观察网络是否能承受。

更精细的队列管理

BBRv2致力于将瓶颈路由器的排队量维持在一个很小的目标值(通常是1个BDP以内)。

  • ProbeBW的演进:它不仅探测带宽上限,还探测队列的承受能力。它会估算因为自己探测行为而增加的额外排队量。如果排队量增长过快,即使没丢包,它也会放缓探测步伐。这使得BBRv2在探测时产生的队列更小、更可控。
  • ProbeRTT的改进:BBRv2认识到ProbeRTT的带宽浪费问题。新的ProbeRTT不再强制将飞行数据降到4个包,而是降低到一个略低于当前估计BDP的水平。这样做既能有效地排空在途队列以测量RTprop,又不会让网络管道完全干涸,从而显著提高了在高BDP网络中的平均吞吐量。

拥塞时的主动退让与公平性

这是BBRv2最关键的改进,解决了与CUBIC等算法的共存问题。

  • 感知持续拥塞:BBRv2会跟踪数据包在网络中的“留存时间”(in-flight time)。如果发现数据包的RTT持续高于RTprop,说明网络中存在持续的排队
  • 主动降低BtlBw估计:当BBRv2确认存在持续拥塞时,它不再固执地坚守自己测量的BtlBw。相反,它会主动地、按比例地调低自己的BtlBw估计值
  • 信息共享:在多条BBRv2流共享一个瓶颈时,它们能够通过感知共同的排队情况,更快地收敛到一个公平的带宽分配点。

BBRv2核心思想

BBRv2在基于建模的基础上,引入拥塞信号作为强约束,主动控制排队

  1. 模型与信号结合: BBRv2不再像v1一样几乎完全无视丢包。它将丢包率ECN标记视为明确的拥塞信号,并用这些信号来判断其带宽模型是否过于激进,从而进行实时约束。
  2. 主动控制在途队列 (Inflight Queue): 将网络中的排队延迟维持在一个非常低的水平。它通过引入inflight_hiinflight_lo两个“水位线”来精确控制在途数据量,以主动管理和消除不必要的排队。
  3. 可证明的公平性: 当BBRv2与CUBIC等基于丢包的算法共享瓶颈时,如果它检测到持续的拥塞(高丢包率或持续排队),它会主动降低自己的带宽估计值 (BtlBw),为其他流量“让路”。
  4. 对无线网络更友好: 通过对拥塞信号的感知,BBRv2能更好地处理无线网络中的随机丢包,避免像v1那样因过于激进的探测而导致性能恶化。

BBRv2实现

状态机

BBRv2 核心变量

  • bw:MaxBwFilter输出的带宽估计值(基于 delivery_rate 样本)。
  • min_rtt:MinRTTFilter 输出的最小 RTT。
  • bdp = bw * min_rtt:带宽时延积(目标 in-flight 数据量)。
  • pacing_rate:发送速率上限。
  • cwnd:拥塞窗口上限。
  • inflight_hi / inflight_lo:可接受的 in-flight 数据上下界
  • bw_hi / bw_lo:带宽估计的上下界
  • extra_acked:用于补偿 ACK 聚合
  • offload_budget:TSO/GSO/LRO 相关的额外预算。

STARTUP 阶段

目标:快速填满管道并探测最大带宽。

  • 进入条件:连接建立或应用恢复发送。

  • pacing_gainBBRStartupPacingGain = 4 * ln(2) ≈ 2.77

  • cwnd_gainBBRStartupCwndGain = 2.0

  • pacing_rate

    pacing_rate = pacing_gain * bw
    
  • cwnd

    inflight = cwnd_gain * bdp + extra_acked
    cwnd = min(cwnd, inflight)
    
  • 退出条件

    1. 探测到带宽已“填满管道”(bw 无显著增长
    2. 丢包率超过阈值(BBRLossThresh ≈ 2%),触发提前退出。
  • 更新变量

    • 如果丢包:inflight_hi = max(inflight * BBRBeta, inflight - lost)BBRBeta ≈ 0.7
    • 否则:继续指数增加 pacing_rate,直到退出。

DRAIN 阶段

目标:把 Startup 期间积累的队列 drain 掉,使 in-flight ≈ BDP。

  • 进入条件:Startup 退出时。

  • pacing_gain

    pacing_gain = 1 / BBRStartupPacingGain ≈ 0.36
    
  • cwnd_gainBBRStartupCwndGain = 2.0

  • pacing_rate

    pacing_rate = pacing_gain * bw
    
  • cwnd

    inflight = cwnd_gain * bdp + extra_acked
    cwnd = min(cwnd, inflight)
    
  • 退出条件: 当 in-flight ≤ BDP(即估计队列清空),进入 ProbeBW。

  • 更新变量

    • 在退出 Drain 时,初始化:

      inflight_hi = inflight   /* 记录此时安全的上界 */
      bw_hi = bw
      

PROBE_BW 阶段

目标:稳态运行,维持高利用率和低延迟,同时持续探测可用带宽。

分为 四个子状态

(a) UP

  • 目标:试探更高带宽。

  • pacing_gain = 1.25

  • cwnd_gain = 2.0

  • pacing_rate

    pacing_rate = 1.25 * bw
    
  • 丢包检测

    if (lost > inflight * BBRLossThresh):
        inflight_hi = max(inflight * BBRBeta, inflight - lost)
        enter DOWN
    
  • 退出条件

    • 没有丢包 → 经过 1 RTT 进入 DOWN。
    • 有丢包 → 立即进入 DOWN。

(b) DOWN

  • 目标:轻度 drain,避免长期积压。

  • pacing_gain = 0.9

  • cwnd_gain = 2.0

  • pacing_rate

    pacing_rate = 0.9 * bw
    
  • 退出条件

    • 队列减少,inflight ≈ BDP → 进入 CRUISE。

(c) CRUISE

  • 目标:稳定发送,维持公平性。

  • pacing_gain = 1.0

  • cwnd_gain = 2.0

  • pacing_rate

    pacing_rate = bw
    
  • 退出条件

    • 定时器触发(约 8–16 个 RTT)→ 重新进入 REFILL,开始下一个 probe cycle。
    • 如果 min_rtt 过期(>10s),进入 ProbeRTT。

(d) REFILL

  • 目标:把 inflight 补回 BDP。
  • pacing_gain = 1.0
  • cwnd_gain = 2.0
  • 退出条件: 当 inflight ≈ BDP,进入 UP。

PROBE_RTT 阶段

目标:排空队列以测量真实 min_rtt。

  • 进入条件min_rtt 超过 MinRTTFilterLen(≈10s)未更新。

  • pacing_gain = 1.0

  • cwnd_gain = 0.5

  • cwnd

    inflight = 0.5 * bdp + extra_acked
    cwnd = max(inflight, BBRMinPipeCwnd)   /* 至少4个MSS */
    
  • 持续时间

    • 至少 200ms,且 ≥1 RTT。
  • 退出条件

    • 测得新的 min_rtt,或超时结束。
    • 返回 ProbeBW。

丢包与 ECN 响应(全局逻辑)

  • 丢包判据

    if lost > inflight * BBRLossThresh (≈ 2%):
        inflight_hi = max(inflight * BBRBeta, inflight - lost)
        if state == ProbeBW_UP:
            enter ProbeBW_DOWN
    
  • ECN 判据:与丢包类似,如果 ECN-CE 标记比例超过阈值,同样下调 inflight_hi/bw_hi。

  • 长期约束

    • pacing_rate 和 cwnd 永远不会超过 bw_hiinflight_hi
    • 也不会低于 bw_loinflight_lo