TCP 拥塞控制解决发送方如何感知网络容量、在不造成拥塞的前提下最大化吞吐的问题,CUBIC 是 Linux 默认算法,BBR 是 Google 2016 年提出的基于带宽探测的新一代算法。
相关文章:Rendezvous Hashing · QUIC 丢包恢复与拥塞控制
目录
| 章节 | 说明 |
|---|---|
| 问题背景 | 为什么需要拥塞控制,核心矛盾是什么 |
| 基础概念 | cwnd、rwnd、ssthresh、RTT 精确定义 |
| 经典 TCP Reno | 四阶段完整伪代码与执行追踪 |
| CUBIC 算法 | 三次函数增长模型,Linux 默认实现 |
| BBR 算法 | 基于带宽探测的主动感知,四状态机 |
| BBR 状态机执行追踪 | 从 STARTUP 到 PROBE_BW 的具体数值演示 |
| CUBIC vs BBR 核心差异 | 设计哲学、适用场景、公平性对比 |
| 异常与边界场景 | 带宽突变、RTT 突变、多流竞争、ECN |
| 参考资料 | 论文、RFC、内核文档 |
问题背景
核心矛盾
TCP 发送方不知道路径上瓶颈链路的带宽,只能通过试探来学习:
- 发送太慢:带宽利用率低,吞吐浪费
- 发送太快:路由器队列溢出,分组丢失,触发重传,进一步加剧拥塞(拥塞崩溃)
1986 年 Van Jacobson 观察到 ARPANET 上吞吐量从 32 Kbps 骤降至 40 bps 的"拥塞崩溃"现象,随后在 RFC 1122/1323 中提出了慢启动与拥塞避免机制。
网络模型假设
发送方 ──── 路径(瓶颈链路 BtlBw)──── 接收方
└── 路由器队列(Buffer)
- 可用带宽 = 瓶颈链路带宽(BtlBw)
- 传播延迟 = 路径最小 RTT(RTprop)
- 队列延迟 = 额外增加的往返时延
基础概念
| 变量 | 全称 | 维护方 | 含义 |
|---|---|---|---|
cwnd |
Congestion Window | 发送方 | 发送方允许在途(未确认)的最大字节数,单位 MSS 或字节 |
rwnd |
Receiver Window | 接收方通告 | 接收方缓冲区剩余空间,通过 TCP 头部 Window 字段传递 |
ssthresh |
Slow Start Threshold | 发送方 | 区分慢启动与拥塞避免阶段的阈值,初始值通常为 64 KB |
RTT |
Round-Trip Time | 发送方测量 | 一个数据段从发出到收到 ACK 的时间,用于计算 RTO |
MSS |
Maximum Segment Size | 协商 | 单个 TCP 段最大数据字节数,通常 1460 字节(以太网) |
BDP |
Bandwidth-Delay Product | 计算值 | BtlBw × RTprop,充满管道所需字节数 |
实际发送窗口:send_window = min(cwnd, rwnd)
经典 TCP Reno
3.1 四个阶段
阶段一:慢启动(Slow Start)
触发条件:连接建立初期,或超时重传后(cwnd 重置为 1 MSS)
// 慢启动:每收到一个 ACK,cwnd 增加 1 MSS → 每 RTT 翻倍(指数增长)
初始化:
cwnd = 1 MSS // 初始拥塞窗口
ssthresh = 64 KB // 初始慢启动阈值(实现相关,RFC 建议更大)
on_ack_received(acked_bytes):
if cwnd < ssthresh:
cwnd += MSS // 慢启动:每 ACK 增加 1 MSS
// 效果:每 RTT 内发 cwnd/MSS 个包,收到 cwnd/MSS 个 ACK
// cwnd += cwnd,即每 RTT 翻倍
else:
cwnd += MSS * MSS / cwnd // 拥塞避免:每 RTT 增加约 1 MSS
// 公式推导:每个 ACK 增加 MSS²/cwnd,
// 一个 RTT 内有 cwnd/MSS 个 ACK,
// 总增加 = (cwnd/MSS) * (MSS²/cwnd) = MSS
阶段二:拥塞避免(Congestion Avoidance)
触发条件:cwnd >= ssthresh
on_ack_received(acked_bytes):
if cwnd >= ssthresh:
cwnd += MSS * MSS / cwnd // 每 RTT 线性增加 1 MSS
阶段三:快速重传(Fast Retransmit)
触发条件:收到 3 个重复 ACK(dup ACK),说明某段丢失但后续段到达
// 重复 ACK 计数器
dup_ack_count = 0
on_ack_received(ack_seq):
if ack_seq == last_ack_seq: // 收到重复 ACK
dup_ack_count += 1
if dup_ack_count == 3: // 三次重复 ACK → 快速重传
retransmit(last_ack_seq) // 立即重传丢失段,不等超时
ssthresh = max(cwnd / 2, 2 * MSS) // 阈值减半
cwnd = ssthresh // 窗口设为新阈值(进入快速恢复)
dup_ack_count = 0
else:
dup_ack_count = 0 // 收到新 ACK,重置计数
// 正常 ACK 处理(慢启动或拥塞避免)
阶段四:快速恢复(Fast Recovery)
触发条件:快速重传后,从 ssthresh 继续拥塞避免(不回到慢启动)
// 快速恢复:cwnd 已设为 ssthresh,直接进入拥塞避免阶段
// 区别于超时:超时后 cwnd 重置为 1 MSS,从慢启动重新开始
on_timeout():
ssthresh = max(cwnd / 2, 2 * MSS)
cwnd = 1 * MSS // 超时:重置为 1,重新慢启动
dup_ack_count = 0
3.2 执行追踪(Reno,MSS=1,ssthresh=8)
RTT 事件 cwnd ssthresh 说明
--- --------------- ----- -------- --------------------------
0 连接建立 1 8 初始状态
1 1个ACK 2 8 慢启动,翻倍
2 2个ACK 4 8 慢启动,翻倍
3 4个ACK 8 8 cwnd==ssthresh,进入拥塞避免
4 8个ACK 9 8 线性+1
5 9个ACK 10 8 线性+1
6 收到3个dup ACK 5 5 ssthresh=10/2=5, cwnd=5
7 从ssthresh继续 6 5 拥塞避免,线性+1
3.3 TCP Reno 的局限性
- 高 BDP 网络:BDP = 100 Mbps × 100ms = 1.25 MB,线性增长需要数百 RTT 才能充满管道
- RTT 不公平:RTT 小的流每秒能做更多次 AIMD 循环,获得更多带宽
- 丢包≠拥塞:无线网络随机丢包也会触发窗口减半
CUBIC 算法
4.1 问题背景
Linux 2.6.19(2006年)将默认拥塞控制从 BIC 改为 CUBIC。核心改进:用三次函数替代线性增长,在高 BDP 网络中快速恢复到历史最大窗口。
4.2 核心数据结构
struct cubic_state {
u32 W_max; // 上次丢包时的 cwnd(历史最大窗口,单位 MSS)
u32 W_last_max; // 上上次丢包时的 W_max,用于快速收敛
u32 K; // cwnd 增长达到 W_max 的时间点(秒)
u32 t_epoch; // 上次丢包/窗口减小的时间戳(秒)
u32 cwnd; // 当前拥塞窗口(MSS)
u32 ssthresh; // 慢启动阈值
// 常数
// C = 0.4(CUBIC 缩放因子,控制增长速度)
// beta = 0.7(乘性减少因子,比 Reno 的 0.5 更保守)
};
4.3 三次函数增长模型
W_cubic(t) = C * (t - K)³ + W_max
其中:
t = 当前时间 - t_epoch(距上次丢包的时间,秒)
K = ∛(W_max * (1 - beta) / C)
= ∛(W_max * 0.3 / 0.4)
C = 0.4(论文推荐值)
beta = 0.7(乘性减少因子)
函数特性:
t=0 时:W_cubic(0) = C*(0-K)³ + W_max = -C*K³ + W_max = W_max*(1-beta) = 0.7*W_max
t=K 时:W_cubic(K) = 0 + W_max(恢复到丢包前水平)
t>K 时:超过 W_max,继续探测更高带宽
4.4 完整伪代码
// CUBIC 初始化
cubic_init():
W_max = 0
K = 0
t_epoch = 0
cwnd = 1 * MSS
ssthresh = INITIAL_SSTHRESH // 通常 64KB / MSS
// 收到 ACK 时更新 cwnd
cubic_on_ack(now):
if cwnd < ssthresh:
cwnd += MSS // 慢启动阶段,指数增长
return
t = now - t_epoch // 距上次丢包的时间(秒)
// 计算 CUBIC 目标窗口
W_cubic_t = C * (t - K)^3 + W_max // C=0.4
// 与 TCP 友好性(Reno 等效窗口)比较,取较大值
// Reno 等效:W_est += MSS * (3*beta/(2-beta)) / cwnd * per_ack
// 实现上简化为:W_est = W_max * beta + 3*(1-beta)/(1+beta) * t/RTT
W_tcp_friendly = W_est(t)
target = max(W_cubic_t, W_tcp_friendly)
if target > cwnd:
cwnd += (target - cwnd) / cwnd // 每 ACK 增量,分摊到一个 cwnd
else:
cwnd unchanged // 不减少
// 检测到丢包(3 个 dup ACK)
cubic_on_loss():
W_last_max = W_max
W_max = cwnd // 记录丢包时窗口
// 快速收敛:如果本次 W_max 小于上次,说明带宽减少,更快收缩
if W_max < W_last_max:
W_max = W_max * (2 - beta) / 2 // W_max = W_max * 0.85
ssthresh = max(cwnd * beta, 2 * MSS) // ssthresh = 0.7 * cwnd
cwnd = ssthresh
// 重新计算 K,t_epoch 重置
K = cbrt(W_max * (1 - beta) / C)
t_epoch = now
// 超时
cubic_on_timeout():
W_max = cwnd
ssthresh = max(cwnd * beta, 2 * MSS)
cwnd = 1 * MSS // 回到慢启动
K = cbrt(W_max * (1 - beta) / C)
t_epoch = now
4.5 执行追踪(CUBIC,W_max=20,beta=0.7,C=0.4,MSS=1)
事件 t(s) K(s) cwnd ssthresh W_cubic(t) 说明
----------- ----- ----- ----- -------- ---------- -------------------
丢包发生 0 2.94 20 14 14.0 K=∛(20*0.3/0.4)≈2.94
0.5 2.94 14 14 14.0-1.27≈12.7 → 保持14(min)
1.0 2.94 14 14 ≈13.4 W_cubic=0.4*(1-2.94)³+20≈13.4
1.5 2.94 15 14 ≈15.9 超过cwnd,增长
2.0 2.94 16 14 ≈18.3 接近W_max,加速
2.94 2.94 20 14 20.0 恢复到W_max
3.5 2.94 21 14 21.3 超过W_max,继续探测
新丢包 3.5 ← 21→14 14 重置 W_max=21, K重新计算
CUBIC vs Reno 增长速度对比(相同 W_max=20,t=2s 时):
- Reno:每 RTT +1,从 14 到 20 需要 6 个 RTT
- CUBIC:t=2s 时 W_cubic≈18.3,仅需约 3s(假设 RTT=200ms 即 15 个 RTT 内完成)
BBR 算法
5.1 设计哲学:从丢包感知到带宽感知
传统算法(Reno/CUBIC):
感知信号 = 丢包(被动等待队列溢出)
问题:
① 浅队列路由器:少量数据就丢包,带宽利用率低
② 无线链路随机丢包:误触发窗口减半
③ 高 BDP 链路:恢复太慢
BBR(Bottleneck Bandwidth and RTT):
感知信号 = 带宽(BtlBw)+ 最小 RTT(RTprop)
目标:以瓶颈带宽发送,不在队列中积压数据(工作在 BDP 点)
5.2 核心数据结构
struct bbr_state {
// 带宽估算(滑动窗口最大值)
u64 btlbw; // 瓶颈带宽估算(bps),= delivered / interval
u64 btlbw_filter; // 10 RTT 内的最大带宽(WindowedMaxFilter)
// RTT 估算
u32 rtprop; // 最小 RTT(ns),= min(RTT) over last 10s
u64 rtprop_stamp; // rtprop 最后更新时间
// 发送速率控制
u64 pacing_rate; // 实际发送速率 = btlbw * pacing_gain
u32 cwnd; // 拥塞窗口 = btlbw * rtprop * cwnd_gain
// 状态机
enum bbr_mode mode; // STARTUP / DRAIN / PROBE_BW / PROBE_RTT
u32 pacing_gain; // 发送速率增益(定点数,1.0=2048)
u32 cwnd_gain; // 窗口增益
// PROBE_BW 轮转
u8 cycle_idx; // pacing_gain 周期索引(0-7)
u64 cycle_stamp; // 当前 pacing_gain 阶段开始时间
// 统计
u64 delivered; // 累计已确认交付字节数
u64 delivered_stamp;// delivered 上次记录时间
};
// pacing_gain 周期(PROBE_BW 状态,8个相位)
// 相位0: 1.25 → 探测更高带宽
// 相位1: 0.75 → 排空多余队列
// 相位2-7: 1.0 → 稳定巡航
pacing_gain_cycle[8] = {1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}
5.3 四个状态
| 状态 | 目标 | pacing_gain | cwnd_gain | 退出条件 |
|---|---|---|---|---|
| STARTUP | 指数探测最大带宽 | 2.885 | 2.885 | btlbw 连续 3 轮无增长 |
| DRAIN | 排空 STARTUP 积压队列 | 1/2.885≈0.35 | 2.885 | in_flight ≤ BDP |
| PROBE_BW | 周期性探测带宽,稳态运行 | 轮转[1.25,0.75,1.0×6] | 2 | PROBE_RTT 触发 |
| PROBE_RTT | 降低 cwnd 测量最小 RTT | 当前值 | 1 | 持续 200ms 后恢复 |
5.4 完整伪代码
// BBR 初始化
bbr_init():
mode = STARTUP
pacing_gain = 2.885 // ≈ 2/ln2,指数增长
cwnd_gain = 2.885
btlbw = 0
rtprop = +∞
rtprop_stamp = now()
cycle_idx = 0
delivered = 0
// 每个 ACK 到达时调用
bbr_on_ack(rtt, delivered_delta, interval):
// 1. 更新带宽估算
bw_sample = delivered_delta / interval // 采样带宽
btlbw = WindowedMax(btlbw_filter, bw_sample, 10 * rtprop) // 10 RTT 窗口最大值
// 2. 更新最小 RTT
if rtt < rtprop:
rtprop = rtt
rtprop_stamp = now()
// 3. 状态机转换
bbr_update_mode()
// 4. 更新发送参数
pacing_rate = btlbw * pacing_gain
cwnd = max(btlbw * rtprop * cwnd_gain, 4 * MSS) // 不低于4MSS
// 状态机转换逻辑
bbr_update_mode():
switch mode:
case STARTUP:
if btlbw_no_growth_for(3_rounds): // 连续3轮带宽不增长
mode = DRAIN
pacing_gain = 1.0 / 2.885 // ≈ 0.35,快速排空
cwnd_gain = 2.885
case DRAIN:
in_flight = packets_in_flight * MSS
if in_flight <= btlbw * rtprop: // 队列已排空
mode = PROBE_BW
cycle_idx = 0
pacing_gain = pacing_gain_cycle[0] // 1.25
cwnd_gain = 2.0
cycle_stamp = now()
case PROBE_BW:
// 每个 RTT 推进一个 pacing_gain 相位
if now() - cycle_stamp >= rtprop:
cycle_idx = (cycle_idx + 1) % 8
pacing_gain = pacing_gain_cycle[cycle_idx]
cycle_stamp = now()
// 检查是否需要 PROBE_RTT
if now() - rtprop_stamp >= 10s: // 10s 未更新最小 RTT
mode = PROBE_RTT
cwnd_gain = 1.0
case PROBE_RTT:
cwnd = max(4 * MSS, ...) // 降低 cwnd
if probe_rtt_done(): // 持续200ms且in_flight≤4MSS
rtprop_stamp = now() // 重置计时器
mode = STARTUP if not_filled_pipe else PROBE_BW
restore_cwnd_gain()
// BBR 对丢包的处理(不触发窗口减半)
bbr_on_loss():
// BBR 不把丢包作为拥塞信号
// 但遵守 TCP 重传机制(快速重传、超时重传)
// cwnd 短暂设为 in_flight(保守),不做乘性减少
cwnd = min(cwnd, in_flight) // 不减半,仅保守处理
5.5 带宽测量细节
// 带宽采样(每个 ACK)
on_ack(ack):
now_delivered = total_delivered // 当前累计交付
now_time = now()
// 记录该数据包发出时的状态
bw_sample = (now_delivered - pkt.delivered) /
(now_time - pkt.delivered_time)
// 使用 Windowed Max Filter(滑动窗口最大值)
// 窗口大小 = 10 RTT,保留近期最大带宽
btlbw = WindowedMax(btlbw, bw_sample, window=10*rtprop)
BBR 状态机执行追踪
场景:100 Mbps 链路,RTprop=20ms,从 STARTUP 开始
时间(ms) 状态 btlbw(Mbps) rtprop(ms) cwnd(KB) pacing_rate(Mbps) 事件
------ ------- ----------- ---------- -------- ----------------- ----
0 STARTUP 0 +∞ 4MSS 0 初始化
20 STARTUP 12 20 ~10 34.6 第1轮ACK,btlbw=12
40 STARTUP 25 20 ~20 72.1 第2轮,btlbw增长
60 STARTUP 50 20 ~40 144.3 第3轮,翻倍
80 STARTUP 100 20 ~80 288.5 接近瓶颈
100 STARTUP 100 20 ~80 288.5 btlbw无增长第1轮
120 STARTUP 100 20 ~80 288.5 btlbw无增长第2轮
140 STARTUP 100 20 ~80 288.5 btlbw无增长第3轮→转DRAIN
140 DRAIN 100 20 ~80 35.0 pacing_gain=0.35
160 DRAIN 100 20 ~50 35.0 排空队列中
180 DRAIN 100 20 25(=BDP) 35.0 in_flight≤BDP→转PROBE_BW
180 PROBE_BW 100 20 50 125.0 phase0: gain=1.25
200 PROBE_BW 100 20 50 75.0 phase1: gain=0.75,排空
220 PROBE_BW 100 20 50 100.0 phase2-7: gain=1.0,稳态
...
10000 PROBE_RTT 100 20 4MSS 100.0 10s未更新rtprop→降cwnd
10200 PROBE_BW 100 20 50 100.0 200ms测量完成,恢复
// BDP 计算:100Mbps × 20ms = 2.5MB → cwnd ≈ 2.5MB × 2(cwnd_gain) = 5MB
// 实际 cwnd(KB) = 100*1000000/8 * 0.020 * 2 / 1024 ≈ 488 KB(图中简化为50KB演示)
PROBE_BW 一轮 pacing_gain 周期(稳态,btlbw=100Mbps,RTprop=20ms)
相位 持续(ms) pacing_gain pacing_rate(Mbps) 效果
---- -------- ----------- ----------------- ------------------
0 20 1.25 125 超速发送,探测带宽上界
1 20 0.75 75 降速,排空phase0积压
2 20 1.0 100 稳定
3 20 1.0 100 稳定
4 20 1.0 100 稳定
5 20 1.0 100 稳定
6 20 1.0 100 稳定
7 20 1.0 100 稳定(周期结束)
// 每个相位持续约 1 RTT(20ms),完整周期 = 8 RTT = 160ms
// 如果phase0探测到更高带宽,btlbw更新,后续稳态速率提升
CUBIC vs BBR 核心差异
7.1 设计哲学对比
| 维度 | CUBIC | BBR |
|---|---|---|
| 拥塞信号 | 丢包(被动,队列溢出后感知) | 带宽+RTT(主动探测,不等丢包) |
| 工作点 | 满队列(高吞吐但高延迟) | BDP 点(平衡吞吐与延迟) |
| 丢包响应 | 窗口减少到 0.7×cwnd | 不减窗口(不视丢包为拥塞信号) |
| RTT 公平性 | 较差(短 RTT 流占优) | 较好(基于带宽不受 RTT 影响) |
| 高 BDP 网络 | 恢复慢(三次函数改善) | 快速充满管道(STARTUP 指数探测) |
| 低延迟要求 | 会在队列中积压数据 | 主动控制队列,延迟更低 |
| 浅队列路由器 | 适应性好(丢包就减速) | 可能加剧丢包(不响应丢包) |
| 与 CUBIC 竞争 | 公平 | BBR 可能占用更多带宽 |
7.2 适用场景
CUBIC 更适合:
- 数据中心内部(短 RTT,浅队列,CUBIC 流之间公平)
- 与大量 CUBIC 客户端混合的环境
- AQM(主动队列管理,如 FQ-CoDel)未部署的网络
BBR 更适合:
- 高 BDP 链路(跨洋、卫星,RTT 100ms+)
- 无线网络(随机丢包不应触发拥塞响应)
- 需要低延迟的场景(视频会议、游戏)
- Google 内部网络、YouTube CDN
7.3 BBR 的已知问题
问题1:浅队列公平性
现象:BBR 流与 CUBIC 流竞争时,BBR 可能获取不成比例的带宽
原因:CUBIC 丢包就减速,BBR 不减速,导致 CUBIC 流被"饿死"
缓解:部署 FQ(公平队列)调度器,per-flow 限速
问题2:RTT 膨胀
BBR v1:PROBE_RTT 期间 cwnd 降至 4MSS,可能导致短暂吞吐下降
BBR v2:改进 PROBE_RTT,减少影响
问题3:多流竞争不公平
同一瓶颈上多条 BBR 流:btlbw 各自独立估算,可能过于乐观
BBR v2:引入丢包信号辅助限速(L_target=2%),改善公平性
异常与边界场景
场景一:带宽突然增加(扩容或路由切换)
初始状态:btlbw=100Mbps,PROBE_BW 稳态运行
带宽变化:路径切换,瓶颈带宽从 100Mbps → 200Mbps
BBR 的处理:
t=0: pacing_rate = 100Mbps(旧 btlbw)
t=RTT: phase0 以 1.25×100=125Mbps 探测
实际吞吐=125Mbps(新带宽充足,无队列积压)
新 bw_sample = 125Mbps → btlbw 更新为 125Mbps
t=2RTT: phase0 再次以 1.25×125=156Mbps 探测
bw_sample ≈ 156Mbps → btlbw=156Mbps
t=N×RTT: 通过 PROBE_BW 周期性探测,btlbw 逐渐逼近 200Mbps
收敛时间:约 log₁.₂₅(200/100) = 3.1 个探测相位 ≈ 3 RTT
特点:无需丢包,主动感知带宽增长
场景二:RTT 突然增加(路由变化或网络抖动)
初始状态:rtprop=20ms,PROBE_BW 稳态
RTT 变化:拥塞或路由切换,RTT 从 20ms → 50ms
BBR 的处理:
t=0: rtprop=20ms(最小 RTT 记录)
当前 RTT=50ms > rtprop,不更新 rtprop
cwnd = btlbw × rtprop × cwnd_gain(用旧 rtprop)
pacing_rate = btlbw × pacing_gain(不变)
问题:cwnd 基于旧 rtprop(20ms),实际 RTT=50ms
in_flight 可能超过 cwnd → 等效于窗口不足,吞吐下降约 60%
10s后:触发 PROBE_RTT(10s 未更新 rtprop)
PROBE_RTT 阶段:
cwnd = 4 MSS(极小值)
持续 200ms 测量 RTT
如果 RTT 真的是 50ms(稳定的新 RTprop)→ rtprop=50ms 更新
如果 RTT 降回 20ms(临时抖动)→ rtprop 仍=20ms,不更新
更新后:cwnd = btlbw × 50ms × 2 = 原来的 2.5 倍,吞吐恢复
场景三:多流竞争(BBR 流公平性)
场景:同一 100Mbps 瓶颈上,2 条 BBR 流(flow1, flow2)
理想情况:各占 50Mbps
实际问题:
flow1 的 btlbw_filter 记录到 80Mbps(历史峰值)
flow2 的 btlbw_filter 记录到 70Mbps
两流共同 pacing_rate = 80+70 = 150Mbps > 100Mbps(链路满载)
→ 队列积压,RTT 增加,但 BBR 不减速(不响应丢包)
缓解措施:
1. FQ 调度器(tc-fq):每流独立限速,防止单流垄断
2. BBR v2:引入 inflight_lo 基于丢包率限制 in_flight
3. ECN:网络设备标记拥塞,BBR v2 可响应 CE 标记
场景四:ECN 配合使用
ECN(Explicit Congestion Notification,RFC 3168):
路由器在队列开始满时,给数据包打 CE(Congestion Experienced)标记
接收方通过 ACK 中的 ECE 标志通知发送方
发送方收到 ECE → 减速(类似丢包处理)
CUBIC + ECN:
收到 ECE → ssthresh = cwnd * 0.7,cwnd = ssthresh
效果:在丢包前提前减速,减少实际丢包率
BBR v1 + ECN:
BBR v1 基本忽略 ECN(设计上不响应丢包/ECN)
可能导致在部署 ECN 的网络中行为不友好
BBR v2 + ECN:
BBR v2 响应 CE 标记,调整 pacing_rate 和 inflight_lo
在 ECN 环境中与 CUBIC 更公平
配置(Linux):
sysctl net.ipv4.tcp_ecn=1 # 启用 ECN
sysctl net.ipv4.tcp_congestion_control=bbr # 使用 BBR
tc qdisc add dev eth0 root fq # 部署 FQ 调度器
参考资料
论文
- CUBIC: Sangtae Ha, Injong Rhee, Lisong Xu. CUBIC: A New TCP-Friendly High-Speed TCP Variant. ACM SIGOPS Operating Systems Review, 2008.
- BBR v1: Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, Van Jacobson. BBR: Congestion-Based Congestion Control. ACM Queue, 2016.
- BBR v2: Neal Cardwell et al. BBRv2: A Model-Based Congestion Control. IETF TSVWG, 2019.
- 原始拥塞控制: Van Jacobson, Michael J. Karels. Congestion Avoidance and Control. ACM SIGCOMM, 1988.
RFC
- RFC 5681 — TCP Congestion Control(Reno 规范)
- RFC 3168 — The Addition of Explicit Congestion Notification (ECN) to IP
- RFC 6582 — The NewReno Modification to TCP's Fast Recovery Algorithm
代码与文档
- Linux 内核 CUBIC 实现:
net/ipv4/tcp_cubic.c - Linux 内核 BBR 实现:
net/ipv4/tcp_bbr.c - Google BBR 技术博客:https://cloud.google.com/blog/products/networking/tcp-bbr-congestion-control-comes-to-gcp-your-internet-just-got-faster
书籍
- Computer Networks: A Systems Approach, Peterson & Davie, Chapter 6
- TCP/IP Illustrated, Volume 1, W. Richard Stevens, Chapter 16
评论 (0)
发表评论