你在数据中心到用户之间的链路上部署了 CUBIC,一切看起来正常。然后用户开始投诉:页面加载慢。你用 ping 测了一下——RTT 从 20ms 涨到了 200ms。但丢包率几乎为零。
问题出在哪里?路径上某个路由器有一个很大的缓冲区。CUBIC 作为基于丢包的算法,不断增大 cwnd,直到缓冲区溢出才降速。在溢出之前,缓冲区被填满了——这就是 Bufferbloat。数据包没丢,但都在路由器队列里排队等着。延迟飙升,吞吐量反而没变。
2016 年,Google 发布了 BBR(Bottleneck Bandwidth and Round-trip propagation time)。它从根本上改变了思路:不看丢包,直接估算瓶颈带宽和最小 RTT,然后按照这两个值控制发送速率。
一、为什么需要 BBR
Kleinrock 的最优操作点
Leonard Kleinrock 在 1979 年的研究指出,网络存在一个最优操作点——在这个点上,吞吐量最大且延迟最低:
# Kleinrock 的网络状态模型:
#
# 吞吐量 ↑ ← BtlBw(瓶颈带宽)
# |─────────────────────────────
# | /
# | / ← 最优操作点在这里
# | / (拥塞开始排队之前的转折点)
# | /
# | /
# | /
# | /
# | /
# |/
# +──────────────────────────→ 飞行中数据量
#
# 延迟 ↑
# | /
# | /
# | /
# | /
# | / ← 丢包开始(CUBIC 在这里反应)
# |──────────────────────/
# |← RTprop(传播延迟) →|
# +──────────────────────────→ 飞行中数据量
#
# 最优操作点 = BDP(带宽延迟积)
# inflight = BtlBw × RTprop 时:
# - 吞吐量 = 最大(管道被填满)
# - 延迟 = 最小(没有排队延迟)
#
# 基于丢包的算法(CUBIC/Reno)在最优点的右边运行:
# 它们填满队列直到丢包,然后回退
# → 永远不会在最优点运行
#
# BBR 的目标:直接在最优操作点运行
# 通过估算 BtlBw 和 RTprop,控制 inflight ≈ BDPCUBIC 在 Bufferbloat 环境中的问题
# Bufferbloat 场景下的 CUBIC vs BBR
# 假设:瓶颈链路带宽 50Mbps, 传播延迟 20ms
# 路由器缓冲区:500ms 等效(很常见的家用路由器)
# CUBIC 的行为:
# 1. cwnd 增长 → 填满缓冲区 → RTT 从 20ms 升到 520ms
# 2. 缓冲区终于溢出 → 丢包 → cwnd 减少到 70%
# 3. cwnd 恢复增长 → 再次填满缓冲区
# 结果:
# 平均吞吐量 ≈ 50Mbps(不错)
# 平均延迟 ≈ 300ms(灾难性的!比传播延迟高 15 倍)
# BBR 的行为:
# 1. 估算 BtlBw = 50Mbps, RTprop = 20ms
# 2. 控制 inflight ≈ BDP = 50Mbps × 20ms = 125KB
# 3. 不多发,不填满缓冲区
# 结果:
# 平均吞吐量 ≈ 50Mbps(同样好)
# 平均延迟 ≈ 20-25ms(接近传播延迟)
# Google 的实测数据(2016, BBR 论文):
# Google.com:
# CUBIC → BBR:中位吞吐量提升 2-25%
# 发展中国家(高 Bufferbloat):吞吐量提升 > 100%
# YouTube:
# RTT 降低 33%(p95)
# 重缓冲率降低 18%二、BBR v1 的核心模型
BtlBw 和 RTprop 的估算
BBR 的核心任务是持续估算两个参数:
# BtlBw(Bottleneck Bandwidth,瓶颈带宽):
# 路径上最慢链路的带宽
# 估算方法:滑动窗口内最大交付速率
# BtlBw = max(delivery_rate) over last 10 RTTs
#
# delivery_rate 的计算:
# 对每个被确认的数据包:
# delivery_rate = delivered_bytes / delivery_interval
# 其中 delivered_bytes = 从发送这个包到收到它的 ACK 之间成功交付的总字节数
# delivery_interval = 对应的时间间隔
# RTprop(Round-trip propagation time,往返传播延迟):
# 没有排队延迟时的最小 RTT
# 估算方法:10 秒窗口内最小 RTT
# RTprop = min(RTT) over last 10 seconds
#
# 为什么用 max(delivery_rate) 和 min(RTT)?
# 在排队时:delivery_rate 可能被低估(包在队列中等待)
# 在排队时:RTT 被高估(包含了排队延迟)
# 取最大 delivery_rate → 最接近真实瓶颈带宽(排队最少时的采样)
# 取最小 RTT → 最接近真实传播延迟(排队最少时的采样)
# 在 ss 中观察 BBR 的估算值
ss -ti dst 10.0.0.2 | grep bbr
# bbr wscale:7,7 rto:204 rtt:20.5/0.3
# bbr:(bw:50.0Mbps,mrtt:20.1,pacing_gain:1,cwnd_gain:2)
#
# bw:50.0Mbps — BBR 估算的瓶颈带宽 (BtlBw)
# mrtt:20.1 — BBR 估算的最小 RTT (RTprop),单位 msBBR 的四个状态
BBR v1 有四个状态,构成一个状态机:
stateDiagram-v2
[*] --> Startup: 连接建立
Startup --> Drain: BtlBw 不再增长<br>(3 个 RTT 连续不增)
Drain --> ProbeBW: inflight ≤ BDP
ProbeBW --> ProbeBW: 持续循环<br>(8 个阶段)
ProbeBW --> ProbeRTT: RTprop 过期<br>(>10s 未更新)
ProbeRTT --> ProbeBW: 200ms 后恢复
note right of Startup: pacing_gain = 2/ln2 ≈ 2.89\ncwnd_gain = 2/ln2\n指数增长探测带宽
note right of Drain: pacing_gain = ln2/2 ≈ 0.35\n排空多余的队列
note left of ProbeBW: pacing_gain 循环:\n[1.25, 0.75, 1, 1, 1, 1, 1, 1]\n稳态运行
note left of ProbeRTT: cwnd = 4\n排空队列测量真实 RTprop
# === 状态 1: Startup(启动) ===
# 类似慢启动,但用 pacing 控制发送速率
# pacing_rate = 2/ln(2) × BtlBw ≈ 2.89 × BtlBw
# 目标:快速发现瓶颈带宽
# 退出条件:连续 3 个 RTT,BtlBw 估算值不再增长
#
# 与慢启动的区别:
# 慢启动用丢包作为退出信号 → 已经导致了排队溢出
# BBR Startup 用带宽停止增长作为退出信号 → 更早发现瓶颈
# === 状态 2: Drain(排空) ===
# Startup 阶段多发的数据会填充队列
# Drain 阶段减速排空这些队列
# pacing_rate = ln(2)/2 × BtlBw ≈ 0.35 × BtlBw
# 退出条件:inflight ≤ BDP(队列排空)
# === 状态 3: ProbeBW(探测带宽) ===
# 稳态运行阶段,占大部分时间
# 使用 8 个阶段的 pacing_gain 循环:
# [1.25, 0.75, 1, 1, 1, 1, 1, 1]
#
# Phase 0 (gain=1.25): 加速,探测是否有更多带宽
# Phase 1 (gain=0.75): 减速,排空 Phase 0 可能导致的排队
# Phase 2-7 (gain=1.0): 巡航,以 BDP 速率发送
#
# 每个 Phase 持续一个 RTT
# 一个完整周期 = 8 × RTT
# === 状态 4: ProbeRTT(探测 RTT) ===
# RTprop 有 10 秒的过期时间
# 如果 10 秒内没有观察到更小的 RTT → 进入 ProbeRTT
# 将 cwnd 降到 4 个 MSS,持续 200ms
# 目的:排空所有队列,测量真正的传播延迟
# 问题:cwnd=4 意味着吞吐量骤降 200ms
# 在 ss 中观察当前状态
ss -ti dst 10.0.0.2
# bbr:(bw:50.0Mbps,mrtt:20.1,pacing_gain:1.25,cwnd_gain:2)
# pacing_gain:1.25 → 当前在 ProbeBW 的 Phase 0BBR 的 Pacing 机制
BBR 与传统拥塞控制的一个根本区别是它主要通过 pacing(节奏控制) 而不是 cwnd 来控制发送速率:
# 传统算法(Reno/CUBIC):
# 控制变量是 cwnd
# 收到 ACK → 发送 cwnd 允许的所有数据 → 突发
#
# BBR:
# 控制变量是 pacing_rate
# pacing_rate = pacing_gain × BtlBw
# 数据包按照 pacing_rate 均匀发送
# cwnd 只是一个安全上限(inflight cap),不是主要控制手段
# BBR 的 pacing 需要内核的 FQ (Fair Queue) 调度器支持
# 检查当前队列调度器
tc qdisc show dev eth0
# qdisc fq 8001: root refcnt 2 limit 10000p flow_limit 100p
# buckets 1024 orphan_mask 1023 quantum 3028b
# initial_quantum 15140 low_rate_threshold 550Kbit
# 如果不是 fq, 配置 fq:
tc qdisc replace dev eth0 root fq
# 为什么 fq 对 BBR 很重要?
# BBR 通过 sk->sk_pacing_rate 告诉内核"以这个速率发送"
# fq 调度器读取这个值,均匀地调度每个包的发送时间
# 没有 fq, BBR 的 pacing 就无法生效
# → BBR 退化为纯 cwnd 控制 → 效果大打折扣
# 在高性能场景中,建议用 fq_codel 或 fq:
tc qdisc replace dev eth0 root fq_codel
# fq_codel 结合了 Fair Queue 和 CoDel(主动队列管理)三、BBR v1 的已知问题
与 CUBIC 的公平性问题
BBR v1 在与基于丢包的算法(如 CUBIC)共享瓶颈链路时存在严重的公平性问题:
# 实测场景:一条 100Mbps 链路,一条 BBR 流 + 一条 CUBIC 流
# 预期:各占 50Mbps
# 实际(BBR v1):
# BBR: ~70-80 Mbps
# CUBIC: ~20-30 Mbps
# BBR 抢占了过多带宽!
# 原因分析:
# 1. BBR 的 ProbeBW 阶段用 gain=1.25 探测
# → 每 8 个 RTT 有 1 个 RTT 多发 25%
# → 这些额外的数据导致路由器队列增长
# 2. CUBIC 看到丢包(被 BBR 的多余数据挤出来的)
# → CUBIC 降低 cwnd
# 3. BBR 看到的 delivery_rate 没变(因为 BBR 自己的包还在正常交付)
# → BBR 不降速
# 4. CUBIC 反复被挤压 → BBR 占据大部分带宽
# 更极端的场景:多条 BBR 流 vs 少量 CUBIC 流
# CUBIC 流可能几乎被饿死
# 这个问题在 BBR v2 和 v3 中有了显著改善ProbeRTT 的吞吐量下降
# ProbeRTT 每 10 秒触发一次, cwnd 降到 4
# 在高带宽链路上这意味着:
# 假设:BtlBw = 1Gbps, RTprop = 10ms
# 正常 inflight = 1Gbps × 10ms = 1.25MB
# ProbeRTT inflight = 4 × 1460 = 5.84KB
# 吞吐量骤降:从 1Gbps 降到 ~4.7Mbps
# 持续 200ms
# 对于长 RTT 链路问题更严重:
# RTprop = 200ms, 200ms 的 ProbeRTT 等于 1 个 RTT
# 在这个 RTT 内几乎没有有效传输
# 多条 BBR 流的 ProbeRTT 不同步时:
# 随机的吞吐量抖动 → 对延迟敏感的应用不友好高丢包率环境的过度激进
# BBR v1 不把丢包当作拥塞信号
# 在真正的拥塞丢包场景中:
# BBR 不降速 → 发更多包 → 更多丢包 → 恶性循环
# 在随机丢包率 > 5% 的环境中:
# BBR v1 的重传率明显高于 CUBIC
# 因为 BBR 不响应丢包,继续按估算的 BtlBw 发送
# 而这些重传包进一步加重了网络负担
# 这在 WiFi 和移动网络中尤其成问题
# 无线链路的丢包不一定是拥塞,但 BBR 也不处理它
# → 大量无意义的重传四、BBR v2 的改进
BBR v2 是一个重大更新,解决了 v1 的多个工程问题:
# BBR v2 的核心改进:
# 1. 引入丢包和 ECN 信号
# v1 完全忽略丢包 → v2 将丢包和 ECN 作为辅助信号
# 当丢包率超过阈值时,BBR v2 会主动降低 inflight_hi
# 这解决了与 CUBIC 共存的公平性问题
# 2. 改进的 inflight 控制
# v2 引入了 inflight_hi 和 inflight_lo 两个边界:
# inflight_hi: 不导致丢包的最大 inflight(带宽上限)
# inflight_lo: 最近观察到的最小有效 inflight
# BBR v2 在这两个边界之间探测
# 3. 更温和的 ProbeBW
# v2 的 ProbeBW 分为两个子阶段:
# ProbeBW_UP: 增加 inflight 探测更多带宽(类似 v1 的 gain=1.25)
# ProbeBW_DOWN: 减少 inflight 排空队列(类似 v1 的 gain=0.75)
# ProbeBW_CRUISE: 稳态(gain=1.0)
# ProbeBW_REFILL: 恢复 inflight 到 inflight_hi
#
# 关键区别:ProbeBW_UP 在检测到丢包时立即退出
# → 不像 v1 那样无视丢包继续探测
# 4. 更好的 ProbeRTT
# v2 的 ProbeRTT 不再将 cwnd 降到 4
# 而是降到 BDP 的一半
# → 吞吐量下降更温和
# BBR v2 的公平性实测:
# 一条 100Mbps 链路,BBR v2 + CUBIC:
# BBR v2: ~55 Mbps(略多,但接近公平)
# CUBIC: ~45 Mbps
# 比 v1 的 80:20 分配好得多五、BBR v3:当前最新版本
BBR v3 是 BBR 的最新迭代,主要增强了 ECN 支持和公平性:
# BBR v3 的主要改进(相比 v2):
# 1. ECN(显式拥塞通知)的深度集成
# v3 使用 ECN 标记作为主要的拥塞信号之一
# 当收到 ECN CE(Congestion Experienced)标记时:
# → 降低 inflight_hi
# → 效果类似丢包信号,但更早(在丢包发生之前)
# 这使得 BBR v3 在 DCTCP 等 ECN 环境中表现更好
# 2. 改进的 Startup 退出
# v3 在 Startup 阶段检测到 ECN 标记时提前退出
# → 减少 Startup 阶段对其他流的影响
# 3. 更精确的模型参数
# v3 对 BtlBw 和 RTprop 的估算做了微调
# 减少了对暂态波动的过度反应
# BBR v3 的内核状态(Linux 6.x)
# BBR v3 在 Linux 内核中是默认的 BBR 版本
# 使用 modprobe tcp_bbr 即可加载
# 没有单独的 "bbr2" 或 "bbr3" 模块名
# 内核版本决定了实际使用的 BBR 版本
# 检查 BBR 版本
# 通过内核版本间接判断:
uname -r
# 5.15.x → BBR v1 (tcp_bbr 模块)
# 6.1+ → BBR v2/v3 (取决于具体版本和补丁)
# 各版本对比表见下文BBR 版本对比
| 特性 | BBR v1 | BBR v2 | BBR v3 |
|---|---|---|---|
| 首次发布 | 2016 (Linux 4.9) | 2019 (实验) | 2023 (Linux 6.x) |
| 丢包响应 | 忽略 | 作为辅助信号 | 作为辅助信号 |
| ECN 支持 | 无 | 基础支持 | 深度集成 |
| 与 CUBIC 公平性 | 差(抢占) | 改善 | 好 |
| ProbeRTT cwnd | 4 MSS | BDP/2 | BDP/2 |
| Startup 退出 | 带宽不增长 | 带宽不增长 + 丢包 | 带宽不增长 + 丢包 + ECN |
| ProbeBW 子状态 | 无 | UP/DOWN/CRUISE/REFILL | UP/DOWN/CRUISE/REFILL |
| inflight 上限 | 2 × BDP | inflight_hi | inflight_hi |
从 CUBIC 迁移到 BBR 的检查清单
# 迁移前检查:
# 1. 内核版本是否支持 BBR
uname -r
# 至少 4.9,推荐 6.1+ (BBR v3)
# 2. BBR 模块是否可用
modprobe tcp_bbr 2>&1
# 无报错即可
# 3. FQ 队列调度器是否可用
tc qdisc add dev lo root fq 2>&1
tc qdisc del dev lo root 2>/dev/null
# 无报错即可
# 迁移步骤(灰度):
# Phase 1: 在 1% 的服务器上启用 BBR,对比指标 24 小时
# Phase 2: 扩大到 10%,确认无回退
# Phase 3: 全量推广
# 需要监控的指标:
# - 吞吐量(应该持平或提升)
# - p50/p95/p99 延迟(应该降低,尤其 p95+)
# - 重传率(BBR v1 可能上升,BBR v3 应持平)
# - CPU 使用率(BBR 的计算开销略高于 CUBIC,但通常 < 1%)
# 回滚方案:
sysctl -w net.ipv4.tcp_congestion_control=cubic
# 只影响新连接,旧连接需等待断开六、BBR 的部署与配置
Linux 上启用 BBR
# === 步骤 1: 加载 BBR 模块 ===
modprobe tcp_bbr
# 验证
lsmod | grep tcp_bbr
# tcp_bbr 20480 0
# === 步骤 2: 设置默认拥塞控制 ===
sysctl -w net.ipv4.tcp_congestion_control=bbr
# 验证
sysctl net.ipv4.tcp_congestion_control
# net.ipv4.tcp_congestion_control = bbr
# === 步骤 3: 配置 FQ 队列调度器(关键!)===
tc qdisc replace dev eth0 root fq
# 验证
tc qdisc show dev eth0
# qdisc fq 8001: root refcnt 2 ...
# === 步骤 4: 持久化配置 ===
# /etc/sysctl.d/99-bbr.conf
cat > /etc/sysctl.d/99-bbr.conf << 'EOF'
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF
sysctl --system
# === 步骤 5: 验证生效 ===
ss -ti dst REMOTE_HOST | grep bbr
# 应该显示 bbr 而不是 cubic需要注意的部署场景
# === 容器环境 ===
# 拥塞控制算法是内核级设置
# 容器共享宿主机内核 → 所有容器使用相同的算法
# 修改需要在宿主机上操作(或使用特权容器)
# Kubernetes 中修改(需要 DaemonSet)
# apiVersion: apps/v1
# kind: DaemonSet
# metadata:
# name: bbr-tuning
# spec:
# template:
# spec:
# hostNetwork: true
# containers:
# - name: tuner
# image: busybox
# command: ["sh", "-c", "sysctl -w net.ipv4.tcp_congestion_control=bbr && sleep infinity"]
# securityContext:
# privileged: true
# === 云环境 ===
# AWS: EC2 实例通常可以直接切换到 BBR
# 注意:ENA 驱动支持 FQ 调度器
# GCP: 默认已启用 BBR(Google 自己的算法)
# Azure: 可以启用,但需要手动配置
# === 负载均衡器 ===
# LB 终止 TCP 连接时,LB 的拥塞控制算法才生效
# L4 LB (IPVS/透传模式): 不涉及(直接转发)
# L7 LB (Nginx/Envoy): 在 LB 服务器上配置 BBR七、BBR vs CUBIC 实测对比
测试方法论
# 科学的 A/B 对比需要控制变量
# === 测试环境 ===
# 两台服务器通过可控链路连接
# 用 tc netem 模拟不同网络条件
# 添加延迟和带宽限制
tc qdisc add dev eth0 root handle 1: tbf rate 100mbit burst 256k latency 400ms
tc qdisc add dev eth0 parent 1: netem delay 50ms
# 添加随机丢包(模拟真实网络)
tc qdisc change dev eth0 parent 1: netem delay 50ms loss 0.1%
# === 测试脚本 ===
for algo in cubic bbr; do
sysctl -w net.ipv4.tcp_congestion_control=$algo
echo "=== Testing $algo ==="
iperf3 -c 10.0.0.2 -t 60 -i 10 --json > /tmp/${algo}_result.json
sleep 5 # 冷却期
done
# === 提取对比数据 ===
for algo in cubic bbr; do
echo "--- $algo ---"
echo -n "Throughput: "
jq '.end.sum_sent.bits_per_second / 1e6' /tmp/${algo}_result.json
echo -n "Retransmits: "
jq '.end.sum_sent.retransmits' /tmp/${algo}_result.json
done典型场景对比结果
# === 场景 1: 数据中心内部(RTT 0.5ms, 10Gbps, 无丢包) ===
# CUBIC: 9.42 Gbps
# BBR: 9.41 Gbps
# 结论:差异不大,CUBIC 更成熟稳定
# === 场景 2: 同城跨机房(RTT 5ms, 1Gbps, 0.01% 丢包) ===
# CUBIC: 940 Mbps
# BBR: 945 Mbps
# 结论:基本相同
# === 场景 3: 跨国链路(RTT 150ms, 100Mbps, 0.1% 丢包) ===
# CUBIC: 72 Mbps
# BBR: 93 Mbps ← BBR 明显更好
# 结论:高 BDP + 丢包,BBR 优势显现
# === 场景 4: Bufferbloat 环境(RTT 20ms, 50Mbps, 路由器缓冲 500ms) ===
# CUBIC: 48 Mbps, 延迟 ~300ms
# BBR: 47 Mbps, 延迟 ~25ms ← 延迟降低 12 倍!
# 结论:BBR 在 Bufferbloat 环境中的延迟优势巨大
# === 场景 5: 高丢包无线网络(RTT 30ms, 20Mbps, 2% 丢包) ===
# CUBIC: 14 Mbps
# BBR v1: 18 Mbps,但重传率高
# BBR v3: 17 Mbps,重传率改善
# 结论:BBR 略好,但 v1 的重传问题需要 v3 修复
# === 场景 6: 混合流(BBR + CUBIC 共享 100Mbps) ===
# BBR v1 + CUBIC: BBR 占 75Mbps, CUBIC 占 25Mbps(不公平)
# BBR v3 + CUBIC: BBR 占 55Mbps, CUBIC 占 45Mbps(基本公平)选型决策矩阵
| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 数据中心内部 | CUBIC | 成熟稳定,BBR 无明显优势 |
| 数据中心内部(ECN 环境) | DCTCP 或 BBR v3 | ECN 避免丢包,低延迟 |
| 面向用户的 Web 服务 | BBR | 减少 Bufferbloat 延迟 |
| 跨国数据传输 | BBR | 高 BDP 链路利用率更高 |
| CDN 边缘节点 | BBR | 用户链路多样,BBR 适应性好 |
| 视频流媒体 | BBR | 低延迟,减少 rebuffer |
| 与 CUBIC 流共存 | BBR v3 | v3 修复了公平性问题 |
| 游戏服务器 | BBR | 延迟敏感场景 |
| 数据库复制(内部网络) | CUBIC | 稳定性优先 |
八、BBR 的监控与诊断
ss 中的 BBR 状态
# 详细的 BBR 状态信息
ss -ti state established dst 10.0.0.2
# 输出示例:
# bbr wscale:7,7 rto:224 rtt:20.3/0.5 ato:40 mss:1448
# cwnd:171 ssthresh:164
# bytes_sent:1568440000 bytes_retrans:14480 bytes_acked:1568425520
# send 97.8Mbps lastrcv:24 lastack:24
# pacing_rate 122.2Mbps delivery_rate 97.5Mbps
# app_limited busy:8400ms
# bbr:(bw:97.5Mbps,mrtt:20.1,pacing_gain:1.25,cwnd_gain:2)
# rcv_space:29200 rcv_ssthresh:1448000 minrtt:20.1
# BBR 特有字段解读:
# bbr:(...) — BBR 算法的内部状态
# bw:97.5Mbps — 估算的瓶颈带宽 (BtlBw)
# mrtt:20.1 — 估算的最小 RTT (RTprop), 单位 ms
# pacing_gain:1.25 — 当前 pacing 增益
# 1.0 → ProbeBW CRUISE(稳态)
# 1.25 → ProbeBW UP(探测更多带宽)
# 0.75 → ProbeBW DOWN(排空队列)
# 2.89 → Startup(初始探测)
# 0.35 → Drain(排空 Startup 的排队)
# cwnd_gain:2 — cwnd 增益因子
# 持续监控 BBR 状态
watch -n 0.5 "ss -ti dst 10.0.0.2 | grep -A3 bbr"常见 BBR 问题诊断
# === 问题 1: BBR 配置了但没生效 ===
# 症状:ss 显示的仍然是 cubic 而不是 bbr
ss -ti | head -3
# cubic wscale:7,7 ... ← 还是 cubic!
# 排查:
# a) BBR 模块是否加载?
lsmod | grep bbr
# 如果为空:modprobe tcp_bbr
# b) 系统设置是否正确?
sysctl net.ipv4.tcp_congestion_control
# 如果不是 bbr:sysctl -w net.ipv4.tcp_congestion_control=bbr
# c) 已建立的连接不会自动切换!
# 只有新连接才使用新设置
# 需要重启应用或等旧连接断开
# === 问题 2: BBR 吞吐量低于预期 ===
# 排查步骤:
# a) 检查是否有 FQ 调度器
tc qdisc show dev eth0
# 如果没有 fq → BBR 的 pacing 不生效
tc qdisc replace dev eth0 root fq
# b) 检查 BtlBw 估算是否准确
ss -ti dst TARGET | grep 'bw:'
# 比较 bw 值和链路实际带宽
# 如果 bw 远小于实际带宽 → 可能有瓶颈
# c) 检查 RTprop 是否合理
ss -ti dst TARGET | grep 'mrtt:'
# 比较 mrtt 和 ping 的最小 RTT
# 如果 mrtt 远大于 ping RTT → BBR 可能没有进入 ProbeRTT
# d) 检查是否被流量控制限制
ss -ti dst TARGET | grep rcv_space
# 如果 rcv_space 很小 → 接收窗口限制了 BBR
# === 问题 3: BBR 导致过多重传 ===
# 症状:切换到 BBR 后重传率明显上升
ss -ti dst TARGET | grep retrans
# bytes_retrans 很大
# 可能原因:
# a) BBR v1 不响应丢包 → 升级到 BBR v3 的内核
# b) 链路丢包率很高(> 5%)→ 考虑退回 CUBIC
# c) FQ 调度器配置问题 → 检查 tc 配置Prometheus 监控 BBR 指标
# 使用 node_exporter 的 textfile collector 采集 BBR 指标
cat > /opt/bbr_metrics.sh << 'SCRIPT'
#!/bin/bash
METRICS_FILE="/var/lib/node_exporter/textfile/bbr.prom"
{
echo "# HELP tcp_bbr_connections Number of TCP connections using BBR"
echo "# TYPE tcp_bbr_connections gauge"
echo "tcp_bbr_connections $(ss -ti | grep -c '^bbr ')"
echo "# HELP tcp_cubic_connections Number of TCP connections using CUBIC"
echo "# TYPE tcp_cubic_connections gauge"
echo "tcp_cubic_connections $(ss -ti | grep -c '^cubic ')"
# 提取所有 BBR 连接的平均 BtlBw
echo "# HELP tcp_bbr_avg_bw_mbps Average BBR estimated bandwidth in Mbps"
echo "# TYPE tcp_bbr_avg_bw_mbps gauge"
avg_bw=$(ss -ti | grep -oP 'bw:\K[0-9.]+' | \
awk '{sum+=$1; n++} END {if(n>0) print sum/n; else print 0}')
echo "tcp_bbr_avg_bw_mbps $avg_bw"
} > "$METRICS_FILE"
SCRIPT
chmod +x /opt/bbr_metrics.sh
# 每 15 秒采集一次
# crontab: * * * * * for i in 0 15 30 45; do sleep $i; /opt/bbr_metrics.sh; done九、BBR 与 QUIC
BBR 最初是为 Google 的 QUIC 协议设计的,两者有天然的契合:
# QUIC 与 BBR 的配合优势:
# 1. 用户态实现
# QUIC 在用户态实现 → 拥塞控制也在用户态
# 可以更快地迭代和部署新版本 BBR
# 不需要升级内核
# 2. 精确的 RTT 测量
# QUIC 每个包都有唯一的 Packet Number(不像 TCP 的 SEQ 可以重传)
# → RTT 测量更精确 → BBR 的 RTprop 估算更准
# TCP 的 Karn 算法问题(重传包无法测 RTT)在 QUIC 中不存在
# 3. 无队头阻塞
# QUIC 的多流多路复用 → 一条流的丢包不影响其他流
# BBR 可以独立控制每条流的拥塞
# 4. 连接迁移
# QUIC 支持连接迁移(IP 地址变化时不断连接)
# BBR 需要适应新路径的 BtlBw 和 RTprop
# QUIC + BBR 的连接迁移后重新探测逻辑更成熟
# Google 的 QUIC 实现(Chromium / Envoy)默认使用 BBR
# 在 HTTP/3 (QUIC) 上 BBR 的效果通常优于在 TCP 上
# 因为 QUIC 提供了更好的 RTT 测量和丢包信息十、工程决策:何时用 BBR
推荐使用 BBR 的场景
# 1. 面向最终用户的服务(Web/API/CDN)
# 用户的网络环境多样:家用路由器、移动网络、跨国链路
# BBR 在 Bufferbloat 环境中的延迟优势对用户体验影响大
# Google、Cloudflare、Akamai 都在使用 BBR
# 2. 高延迟高带宽的跨地域传输
# 跨洲际数据复制、CDN 回源
# CUBIC 的恢复速度在高 BDP 下仍然不够快
# BBR 直接按估算带宽发送,不需要慢慢爬升
# 3. 视频流媒体服务
# 视频流的质量对延迟和吞吐量都敏感
# BBR 减少的 Bufferbloat 直接降低 rebuffer 率
# YouTube 使用 BBR 后 rebuffer 率下降 18%不推荐使用 BBR 的场景
# 1. 纯数据中心内部通信
# 数据中心内部链路质量可控,Bufferbloat 不严重
# CUBIC 更成熟稳定,出问题时社区经验更丰富
# 如果数据中心支持 ECN → 考虑 DCTCP
# 2. 高丢包率的不可控网络(且无法升级到 BBR v3)
# BBR v1 在高丢包率下重传过多
# 如果内核版本较老(< 6.x),CUBIC 可能更安全
# 3. 与大量 CUBIC 流共存的环境(BBR v1)
# BBR v1 的公平性问题会挤压 CUBIC 流
# 升级到 BBR v3 或统一使用一种算法十一、案例:CDN 边缘节点启用 BBR 的效果
# 背景:某 CDN 服务商在边缘节点从 CUBIC 切换到 BBR
# 节点分布:全球 30+ PoP,主要服务静态资源和视频
# 内核版本:6.1 (BBR v3)
# === 切换前基线(CUBIC, 采集 7 天平均) ===
# 全球平均 TTFB: 85ms (p50), 340ms (p95)
# 视频 rebuffer 率: 2.3%
# 平均吞吐量: 45 Mbps(单流)
# 重传率: 0.8%
# === 灰度切换(10% 节点,7 天对比) ===
# BBR 组 vs CUBIC 组对比:
# CUBIC BBR 变化
# TTFB p50: 85ms 78ms -8.2%
# TTFB p95: 340ms 220ms -35.3% ← 高延迟用户收益最大
# TTFB p99: 1200ms 480ms -60.0%
# 视频 rebuffer: 2.3% 1.9% -17.4%
# 平均吞吐 (单流): 45Mbps 48Mbps +6.7%
# 重传率: 0.8% 0.9% +12.5% ← 略有上升,可接受
# 分地区差异:
# 北美/欧洲(低 Bufferbloat): TTFB 降低 5-10%
# 东南亚/非洲(高 Bufferbloat): TTFB 降低 40-60%
# 移动网络用户: TTFB 降低 30-50%
# 结论:全量推广 BBR,p95+ 延迟改善最为显著
# 对 Bufferbloat 严重的发展中地区用户影响最大十二、结论
BBR 代表了拥塞控制的范式转换:从”观察丢包然后反应”到”主动估算网络容量然后控制”。这个转变的工程影响是深远的——在 Bufferbloat 普遍存在的互联网上,BBR 能同时提供高吞吐和低延迟。
几个关键认知:
BBR 不是银弹。 它在 Bufferbloat 环境中优势巨大,但在数据中心内部或低延迟可控网络中,与 CUBIC 差距不大。选择算法要看具体场景。
FQ 调度器是 BBR 正常工作的前提。 没有 FQ, BBR 的 pacing 机制无法生效,效果大打折扣。部署 BBR 时必须同时配置
tc qdisc replace dev eth0 root fq。版本很重要。 BBR v1 的公平性和重传问题在 v3 中得到了显著改善。如果可能,使用最新的内核版本以获得 BBR v3。
监控 BBR 的状态。
ss -ti提供了 BtlBw、RTprop、pacing_gain 等关键指标。部署后持续监控这些值,确保 BBR 的估算与实际网络状况一致。BBR 和 QUIC 是天然搭档。 QUIC 的精确 RTT 测量和用户态实现使 BBR 能发挥最大效果。如果你在使用 HTTP/3, BBR 几乎是默认最佳选择。
拥塞控制只是 TCP 性能的一个维度。下一篇我们将系统性地讲解 TCP 调优——把 sysctl 参数、socket 选项和拥塞控制串起来,形成完整的调优方法论。
上一篇:TCP 拥塞控制经典算法:从 Reno 到 CUBIC
下一篇:TCP 调优实战:内核参数与 socket 选项完全指南
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【网络工程】TCP 拥塞控制经典算法:从 Reno 到 CUBIC
TCP 拥塞控制是互联网流量管理的核心机制。本文从 AIMD 的数学直觉出发,逐步剖析 Reno、NewReno、BIC、CUBIC 的演进动机与工程差异,通过内核参数观测和实测数据帮助读者理解拥塞窗口行为、选择合适的拥塞控制算法。
【Linux 网络子系统深度拆解】TCP 内核实现(下):数据传输与拥塞控制
tcp_sendmsg 把用户数据拷到 sk_buff 就完事了?远没有。后面还有 Nagle 合并、TSQ 限流、cwnd/rwnd 双窗口门控、RACK-TLP 丢包检测、拥塞状态机五态跳转、sk_pacing_rate 软件限速。本文从 Linux 6.6 内核源码拆解 TCP 数据传输的完整路径——从 send() 到 ACK 处理——以及拥塞控制框架 tcp_congestion_ops 的可插拔架构。
【网络工程】内核网络参数调优:sysctl 全景与实战
Linux 内核网络参数是系统网络性能的基础旋钮。本文从 /proc/sys/net/ 的参数体系出发,系统讲解收发缓冲区自动调优、TCP Backlog 队列、conntrack 连接追踪表、SYN Flood 防护参数、TIME_WAIT 管理,以及参数调优的系统化方法论——先基准、再调整、后验证。
【网络工程】TCP 连接管理:三次握手、四次挥手与状态机
深入剖析 TCP 连接的完整生命周期——三次握手的每个细节、四次挥手的工程陷阱、11 个状态的实测观察,以及 TIME_WAIT 堆积、SYN Flood 防御、端口复用等生产环境高频问题的系统化解决方案。