跳到内容

第 4 卷 / 第 10 章 / 第 2 课

10.2 CUBIC、BBR、发送节奏与主动队列管理 ​

下载速度很高,交互请求却越来越慢,常见原因是瓶颈前积起了长队。拥塞控制决定发送多少,pacing 决定何时发,队列管理决定如何排队、标记和丢包。本课分开这三项职责,比较 CUBIC、BBR 与 AQM,再用数量级计算检查排队延迟。

1. CUBIC 用时间驱动三次曲线增长 ​

RFC 9438 定义了标准轨道的 CUBIC。它通常归为基于丢包的拥塞控制算法,也会响应 ECN 等拥塞信号。记忆中的拥塞窗口为 W_max,窗口减小后,目标值主要随本轮拥塞避免开始后的时间 t 沿三次曲线增长:

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

C 决定曲线的尺度,K 表示在没有新拥塞事件时,曲线回到 W_max 所需的时间。它形成三个区间:

  • 明显低于 W_max 时较快恢复;
  • 接近上次工作点时放缓增长;
  • 超过 W_max 后继续探测剩余容量。

完整 CUBIC 还有 Reno 友好区间、快速收敛、应用供数不足时的处理和精度要求。只计算一次三次公式并取 max(current, target),不足以实现整个算法。

RFC 中的 beta_cubic 表示减小后保留的比例,推荐值为 0.7。例如窗口为 100 SMSS 时,乘以 0.7 留下 70 SMSS,而不是留下 30。CUBIC 减少部分 RTT 偏差,但 ACK 节奏、丢包、pacing、队列和其他算法共存仍会影响公平性。RFC 9438

CUBIC 在许多 Linux 系统中可用,但本机默认值和连接实际算法都应查询,不能从发行版名称推断。

2. BBR 估计交付速率与最小 RTT ​

BBR 系列主要建立两个路径模型:

  • 瓶颈带宽估计,来自近期交付速率样本;
  • 最小 RTT 估计,尝试减少排队延迟对基础路径时延的影响。

带宽与最小 RTT 的乘积给出 BDP 估计,再用于设置发送速率和在途量上限。BBR 还需要启动、排空、带宽探测和 RTT 探测等阶段,以及 ACK 聚集、应用供数不足和卸载批量等修正,不能简化成始终发送一个固定 BDP。

BBR 并非完全忽略丢包。不同版本会利用丢包率和在途量上界控制过度探测;v1、v2、v3 的策略不同,一个固定的“每 8 RTT 轮流乘 1.25 和 0.75”模型不能代表整个系列。

这里的版本依据是工作组草案 draft-ietf-ccwg-bbr-06,描述 BBRv3,仍是实验性工作。该版没有规定专门的 ECN 响应算法;若连接通过 ECT 宣告支持 ECN,仍必须把 CE 标记当作拥塞信号处理,不能默默忽略。BBR 草案 §3.7

基于模型不保证自动公平或完全无队列。与基于丢包的流共享尾丢弃瓶颈、RTT 不同或存在限速器时,都需要实测。记录内核和 BBR 实现版本,否则“用了 BBR”还不足以复现实验。

3. Pacing 分散突发,不增加带宽 ​

一批 ACK 同时到来时,窗口允许的新数据可能一次性发出。Pacing 按目标速率把发送分散到时间上,减少微突发、队列尖峰和同步丢包。

速率设得太高仍会排队,太低则浪费容量。计时粒度、网卡卸载、队列规则和应用突发都会影响线上间隔。启用 TSO 后,内核交给网卡的大段数据如何切分与发送,也要看硬件的 pacing 能力。

4. Bufferbloat 要看负载下的排队延迟 ​

缓冲可以吸收短突发,长期积压却会抬高 RTT。如果发送端只能等尾丢弃队列装满才收到拥塞信号,交互流量可能在吞吐仍很高时承受很长的额外等待。

相同队列字节数,在不同带宽下的等待时间差别很大;Wi-Fi 和移动网络的速率变化尤其值得留意。先用下面的关系估算量级:

text
queueing delay ≈ queued bytes / bottleneck service rate

它没有模拟调度、包长、无线重传和速率变化。应结合负载下的延迟观测,不能只看到缓冲配置大,就直接断言发生了 bufferbloat。

5. AQM 在队列溢出前标记或丢包 ​

主动队列管理(Active Queue Management,AQM)根据队列状态提前标记 ECN 或丢包,为发送端提供反馈,而不是只在缓冲耗尽时才采取动作。

CoDel 根据报文在队列中的停留时间判断持续排队,而非只看队列字节数。它有 target、interval 等参数和实现限制;目标是减少对带宽等环境参数的人工调优,不能理解成完全没有配置。RFC 8289

FQ-CoDel 先把流分配到多个队列并调度,再分别应用 CoDel,避免一个大流的积压压住稀疏交互流。散列碰撞、隧道把多流聚成一流以及不响应拥塞的流仍需考虑。RFC 8290 CAKE 进一步组合整形、DiffServ、主机隔离和链路开销补偿。iproute2 CAKE 手册

AQM 需要控制真正的瓶颈队列,或通过整形把瓶颈放在自己能管理的位置。若更慢的队列藏在运营商设备中,只修改高速 LAN 接口的 qdisc,未必能减少那里的等待。

6. 先观测,再比较算法 ​

以下为 Linux 的只读检查,依赖 procps 和 iproute2;它们不会切换算法或修改队列:

bash
# 查询当前可用算法与默认算法
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control

# 查看连接的 RTT、cwnd、重传与发送速率
ss -tin

# 查看队列规则、丢包、标记和积压计数
tc -s qdisc show

ss 中 cwnd:100 通常以段为单位,还需结合 MSS、在途量、应用是否供数不足、pacing、rwnd 和 RTT 解读。字段随内核和算法变化;可用算法列表也可能只列出已注册的算法,并不等于内核还能加载哪些模块。

比较 CUBIC、BBR 或 AQM 时,应控制:

  • 瓶颈速率、基础 RTT、队列大小与 qdisc;
  • 丢包、乱序和 ECN 策略;
  • 流数、启动时间和不同流的 RTT;
  • 收发端内核版本及卸载配置;
  • 预热和测量时长;
  • 吞吐、p50/p95/p99 延迟、重传、公平性与 CPU 指标。

一次文件下载无法支持算法优劣的普遍结论。实验应在独立测试环境、网络命名空间或单套接字配置中进行;共享生产主机上的全局 sysctl 变更会影响其他连接,需要单独的变更与回退安排。

7. 计算 BDP 与队列排空时间 ​

下面的 Python 3 示例使用十进制 Mbps、MB 和有限数值输入。保存为 queue_math.py,运行 python3 queue_math.py;代码只做量级计算,不配置网络:

python
def bdp_bytes(rate_mbps: float, base_rtt_ms: float) -> float:
    if rate_mbps <= 0 or base_rtt_ms <= 0:
        raise ValueError("rate and RTT must be positive")
    return rate_mbps * 1_000_000 / 8 * base_rtt_ms / 1_000

def queue_delay_ms(queued_bytes: int, rate_mbps: float) -> float:
    if queued_bytes < 0 or rate_mbps <= 0:
        raise ValueError("invalid queue/rate")
    return queued_bytes * 8 / (rate_mbps * 1_000_000) * 1_000

assert bdp_bytes(100, 20) == 250_000
assert queue_delay_ms(1_000_000, 20) == 400
print(bdp_bytes(100, 20))
print(queue_delay_ms(1_000_000, 20))

预期输出:

text
250000.0
400.0

100 Mbps、基础 RTT 为 20 ms 的路径,BDP 约为 250000 字节;20 Mbps 的瓶颈排空 1 MB 积压数据,需要约 400 ms。它是流体模型估算,不是报文调度或无线链路模拟器。带宽很忙,用户也可能一直在等。

8. 验收问题 ​

  1. W_max、C 与 K 分别怎样影响 CUBIC 曲线?
  2. 为什么不能说 BBR 完全不看丢包,为什么还要注明实现版本?
  3. Pacing 为什么能降低突发,却不能创造容量?
  4. CoDel 与 FQ-CoDel 如何区别?
  5. 为什么控制不到瓶颈队列的 AQM 可能无法降低排队延迟?

参考 ​

Built with VitePress | Software Systems Atlas