4. 端到端拥塞控制:BBR算法、CUBIC算法、Vegas算法

端到端拥塞控制,说白了就是发送端自己判断网络是不是堵了,然后决定是踩油门还是踩刹车。我这些年调过不少TCP栈,见过最典型的三种算法就是BBR、CUBIC和Vegas。它们思路完全不同,适用场景也各有千秋。今天咱们就掰开揉碎了聊聊。

4.1 传统算法的困境:丢包就是信号?

在讲具体算法之前,我想先说说一个老观念——「丢包等于拥塞」。这个想法在早期网络里是对的,但现在呢?你想想看,无线网络、数据中心、长肥网络(LFN)里,丢包可能只是因为信号干扰或者缓冲区溢出,跟真正的链路拥塞没啥关系。

我遇到过好几次这样的坑:某次在数据中心调优,明明链路带宽还有富余,但CUBIC一检测到丢包就猛降窗口,结果吞吐量直接腰斩。后来我才意识到,问题出在交换机缓冲区太小,微突发就把包丢了。嗯,这里要注意,丢包≠拥塞,这个认知是理解BBR和Vegas的关键。

4.2 CUBIC算法:二进制的拥塞窗口

CUBIC是目前Linux默认的拥塞控制算法。它的核心思路很简单:窗口增长函数是一个三次函数(立方函数),所以叫CUBIC。

核心思想: 丢包后窗口减半,然后以三次函数曲线恢复。窗口越大,恢复越快。

具体来说,CUBIC维护一个参数 W_max,表示上次丢包时的窗口大小。丢包发生后,窗口降到 0.7 * W_max,然后按照以下公式增长:

W(t) = C * (t - K)^3 + W_max

其中 t 是距离上次丢包的时间,K = (W_max * (1 - β) / C)^(1/3),β 是乘性减窗因子(通常0.3),C 是常数。

这个公式有什么好处?我举个例子:

  • 窗口较小时:三次函数增长缓慢,避免频繁丢包
  • 窗口接近W_max时:增长变缓,探测瓶颈带宽
  • 窗口超过W_max后:增长加速,快速抢占带宽

我个人习惯把CUBIC比作「二进制搜索」——它在丢包点附近反复试探,找到当前网络的容量上限。但问题也在这里:它完全依赖丢包作为拥塞信号。在高速网络里,丢包前可能已经排队很久了,延迟早就飙升了。

避坑指南: 我曾经在一条100Gbps链路上调优,CUBIC的窗口增长到几十万包才丢一次。丢包后窗口减半,然后又要花几十秒才能恢复。这种场景下,CUBIC的收敛速度太慢了。

4.3 Vegas算法:基于延迟的拥塞控制

Vegas是另一种思路——它不看丢包,而是看RTT(往返时延)的变化。为什么?因为网络一旦开始排队,RTT就会增加。Vegas认为,RTT增加才是拥塞的早期信号。

Vegas维护两个关键参数:

  • BaseRTT:测量到的最小RTT,代表无拥塞时的延迟
  • Expected Throughput:窗口大小 / BaseRTT,即理论最大吞吐
  • Actual Throughput:窗口大小 / 当前RTT,即实际吞吐

然后计算差值:

diff = (Expected - Actual) * BaseRTT

这个 diff 代表当前网络中排队的数据包数量。Vegas的策略是:

diff 值 动作
diff < α 窗口加1(带宽还有余量)
α ≤ diff ≤ β 窗口不变(刚好合适)
diff > β 窗口减1(排队太多)

α 和 β 通常取2和4。说白了,Vegas想让网络中排队的数据包控制在2到4个之间。这个思路很优雅——它避免了缓冲区膨胀(Bufferbloat)。

我的经验: 在延迟敏感的场景(比如实时视频、在线游戏),Vegas比CUBIC表现好得多。但Vegas有个致命弱点:它需要准确的BaseRTT。如果路由路径变了,BaseRTT就不准了,算法会误判。

4.4 BBR算法:基于模型的拥塞控制

BBR(Bottleneck Bandwidth and Round-trip propagation time)是Google在2016年提出的。它不依赖丢包,也不直接依赖RTT变化,而是建立一个网络模型:

  • 瓶颈带宽(BtlBw):链路能承载的最大发送速率
  • 往返传播时间(RTprop):无拥塞时的最小RTT

BBR的核心公式是:

发送速率 = min(瓶颈带宽, 窗口大小 / RTprop)

BBR的工作流程分为四个阶段:

  1. 启动(Startup):指数增长,探测瓶颈带宽
  2. 排空(Drain):降低发送速率,排空队列
  3. 带宽探测(ProbeBW):周期性提高速率,检查带宽是否变化
  4. RTT探测(ProbeRTT):周期性降低速率,测量最小RTT

我画了一张流程图,帮你理解BBR的状态机:

BBR算法状态机 Startup 指数增长 Drain 排空队列 ProbeBW 带宽探测 ProbeRTT RTT探测 发现瓶颈带宽 队列排空 周期性触发 RTT测量完成 关键参数: BtlBw:瓶颈带宽 RTprop:最小RTT 增益系数:1.25/0.75 特点: 不依赖丢包 主动探测带宽 避免缓冲区膨胀

BBR的厉害之处在于,它通过周期性探测来维持对网络状态的认知。在ProbeBW阶段,它会以1.25倍速率发送,然后降到0.75倍,通过观察RTT变化来判断带宽是否变化。

核心优势: BBR在高速网络(10Gbps+)和长肥网络(高延迟高带宽)中表现极佳。它不会因为丢包就乱降速,也不会因为缓冲区大就占满队列。

4.5 三种算法的对比

我整理了一张对比表,方便你快速理解:

特性 CUBIC Vegas BBR
拥塞信号 丢包 RTT增加 带宽/RTT模型
缓冲区占用 高(容易填满) 低(控制在α~β) 低(主动排空)
公平性 好(与标准TCP兼容) 较差(容易被CUBIC抢占) 中等(需要多流共存调优)
高速网络 一般(丢包恢复慢) 差(RTT测量不准) 优秀(主动探测)
无线网络 差(误判丢包) 中等(受RTT抖动影响) 好(区分拥塞和丢包)
部署难度 低(默认算法) 低(内核支持) 中(需内核≥4.9)

4.6 实际选型建议

说了这么多,到底该用哪个?我个人习惯这样选:

  • 通用互联网服务:用CUBIC。兼容性最好,大部分场景够用。
  • 延迟敏感应用:用Vegas或BBR。比如视频会议、在线游戏,Vegas的延迟控制很稳。
  • 数据中心/高速链路:用BBR。我曾在10Gbps链路上测试,BBR比CUBIC吞吐量高30%以上。
  • 无线/移动网络:用BBR。它能区分无线丢包和拥塞丢包,不会乱降速。
注意: BBR虽然好,但不是万能的。在低延迟、小缓冲区场景下,BBR的ProbeBW阶段可能会造成短暂抖动。我曾经在5ms延迟的局域网里遇到过,BBR的速率波动反而比CUBIC大。

4.7 小结

三种算法代表了三种不同的拥塞控制哲学:CUBIC相信丢包,Vegas相信延迟,BBR相信模型。没有的好坏,关键看你的网络环境和业务需求。

最后说一句:调拥塞控制算法,一定要结合你的实际流量特征。别光看理论,拿真实数据跑一跑,你会有更深的体会。

小技巧: Linux下可以用 sysctl net.ipv4.tcp_congestion_control 查看当前算法,用 modprobe tcp_bbr 加载BBR模块。切换算法后记得用 ss -ti 观察每个连接的拥塞状态。

可可精品资料库