引言:为什么TCP拥塞控制是网络性能的核心
TCP拥塞控制是互联网稳定运行的基石。从1988年Van Jacobson提出慢启动和拥塞避免算法以来,Linux内核经历了Reno → CUBIC → BBR三代核心算法演进。在数据中心网络(10Gbps/100Gbps)、长肥管道(LFN)、5G边缘计算等场景中,拥塞控制算法的选择直接影响吞吐量和延迟。
本文将从内核源码角度,深入剖析Linux TCP拥塞控制的完整实现:从基础理论框架(AIMD)、到CUBIC的三次函数增长曲线、再到BBR的带宽探测模型,最终给出生产环境的调优决策树。
1. 拥塞控制基础理论框架
1.1 问题定义
拥塞控制的核心矛盾:
- 发送方不知道网络路径上的可用带宽
- 如果发送太快 → 网络中间设备(路由器)队列溢出 → 丢包 → 延迟抖动
- 如果发送太慢 → 带宽利用率低 → 吞吐量下降
关键指标:
- 带宽(Bandwidth):路径最大传输速率
- RTT(Round Trip Time):数据包往返时间
- BDP(Bandwidth-Delay Product)= 带宽 × RTT
= 管道中"在途"(In-Flight)数据量的理论最优值
如果 In-Flight > BDP → 队列膨胀 → 缓冲区膨胀(Bufferbloat)
如果 In-Flight < BDP → 带宽未充分利用
1.2 AIMD原则
AIMD(Additive Increase Multiplicative Decrease):
- 加法增大:每个RTT增加1个MSS(Maximum Segment Size)
- 乘法减小:检测到丢包时,窗口减半
效果:发送速率呈锯齿状逐渐逼近最优值
WW
/\ /\ /\ /\
/ \ / \ / \ / \
/ \/ \/ \/ \/
数学证明:AIMD是收敛到公平分配的(Chiu & Jain, 1989)
1.3 内核拥塞控制接口
// include/net/tcp.h — 拥塞控制操作结构体
struct tcp_congestion_ops {
struct list_head list;
u32 key;
u32 flags;
/* 初始化/清理 */
void (*init)(struct sock *sk);
void (*release)(struct sock *sk);
/* 核心回调:ACK到达时计算新窗口 */
u32 (*ssthresh)(struct sock *sk); // 慢启动阈值
void (*cong_avoid)(struct sock *sk, u32 ack); // 拥塞避免:窗口增长
void (*set_state)(struct sock *sk, u8 new_state); // 状态变化通知
/* 丢包/延迟事件 */
void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev);
void (*in_ack_event)(struct sock *sk, u32 flags);
/* 窗口撤销(用于PRR、RACK等精确恢复) */
u32 (*undo_cwnd)(struct sock *sk);
void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample);
/* 可选 */
u32 (*min_tso_segs)(struct sock *sk);
void (*cong_control)(struct sock *sk, const struct rate_sample *rs);
size_t size; // eBPF扩展用
};
// 全局注册/注销:
int tcp_register_congestion_control(struct tcp_congestion_ops *ops);
void tcp_unregister_congestion_control(struct tcp_congestion_ops *ops);
2. 经典算法:Tahoe / Reno / NewReno
2.1 TCP Tahoe (1988)
// net/ipv4/tcp_tahoe.c(已移除,保留在历史中)
// Tahoe = 慢启动(Slow Start) + 拥塞避免(Congestion Avoidance) + 快速重传
Tahoe 的状态机:
────────────> Slow Start <──────────
│ (指数增长) │
│ │ 达到 ssthresh
│ v
│ Congestion Avoidance
│ (线性增长 +1/cwnd)
│ │
│ │ 3个重复ACK(Fast Retransmit)
│ v
│ ssthresh = cwnd/2
│ cwnd = 1 MSS ←── 直接回到慢启动!
└────────────────┘
关键缺陷:快速恢复(Fast Recovery)缺失
→ 每次丢包都要回退到cwnd=1,高带宽延迟积(BDP)下吞吐恢复极慢
2.2 TCP Reno (1990)
// Reno = Tahoe + 快速恢复(Fast Recovery)
Reno 的改进:
1. 收到3个重复ACK时
- ssthresh = cwnd/2
- cwnd = ssthresh + 3(而非1)
2. 进入 Fast Recovery:
- 每收到一个DupACK,cwnd += 1(继续发送新包)
- 维持"管道"不排空
3. 收到新ACK后退出Fast Recovery
- cwnd = ssthresh(直接进入拥塞避免)
Reno 锯齿:
/\
/ \ Fast Recovery
/ \ +-------------+
/ \ /
/ \ /
/ \ /
/ \ /
/ \______/
时间 →
2.3 NewReno改进
Reno的问题:多个数据包丢失时快速恢复只修复第一个丢失包
NewReno(RFC 6582, 2012):
- 引入"恢复点"(Recovery Point)概念
- 等待所有丢失包都被确认后才退出Fast Recovery
- 在部分ACK(Partial ACK)时重传下一个丢失包,但不缩减窗口
// include/net/tcp.h
struct tcp_sock {
u32 snd_ssthresh; // 慢启动阈值
u32 snd_cwnd; // 拥塞窗口
u32 snd_cwnd_cnt; // 拥塞避免期间的计数器
u32 snd_cwnd_clamp; // 窗口上限
u32 snd_cwnd_used; // 当前实际使用窗口
u32 snd_cwnd_stamp; // 窗口改变时间戳
u32 prior_cwnd; // 进入拥塞前窗口(用于undo)
u32 prr_delivered; // PRR: 已交付数据量
u32 prr_out; // PRR: 当前在途数据量
u16 recover_sack; // NewReno: 恢复标记
// ...
};
3. CUBIC:Linux默认的高带宽算法(2006-至今)
3.1 设计动机
CUBIC的目标:在高BDP(带宽延迟积)网络中取代Reno
Reno/CWND变化:每RTT+1,每丢包/2 → 窗口变化是线性的
问题:在100ms RTT、10Gbps链路上:
BDP = 10Gbps × 100ms = 125MB
以Reno速率增长(每RTT+1MSS ≈ 1460B),需要:
125MB / 1460B = ~85,000个RTT才能恢复到满带宽
= 85,000 × 100ms = 8,500秒 ≈ 2.36小时
CUBIC的解:用三次函数代替线性增长
3.2 CUBIC三次函数
// net/ipv4/tcp_cubic.c — CUBIC核心公式
// CUBIC窗口增长函数:
W(t) = C × (t - K)³ + W_max
其中:
- t: 距离上次拥塞事件的时间
- W_max: 上次丢包事件前的cwnd(作为目标窗口)
- K = ³√(W_max × β / C) — "拐点"(inflection point时间偏移)
- C: 伸缩因子(默认0.4)
- β: 窗口缩减因子(默认0.2,即丢包后cwnd降到80%)
曲线特征:
╱
凹增长 ╱ 凸增长
(慢加速) ╱ (快逼近)
╱──────── W_max
╱
╱
凸增长 ╱ 凹增长
快降到新K 慢回稳
关键设计:
1. K拐点之前:凹函数(加速追赶W_max)
2. K拐点之后:凸函数(减速接近W_max)
3. 达成无RTT公平性:增长与RTT无关
3.3 内核实现剖析
// net/ipv4/tcp_cubic.c — 核心函数
static inline void bictcp_update(struct bictcp *ca, u32 cwnd,
u32 acked, struct rate_sample *rs)
{
u64 t; // 经过的时间(μs)
u32 delta, target, offsm;
// 计算距离上次减少的时间
t = tcp_jiffies32 - ca->epoch_start;
if (t < ca->K) {
// K之前区域:凹函数(追赶W_max)
delta = (ca->K - t);
} else {
// K之后区域:凸函数(逼近W_max)
delta = (t - ca->K);
}
// 三次方计算:(delt)^3
offsm = (delta * delta * delta) >> 10; // 位移避免浮点
if (t < ca->K) {
target = ca->last_max_cwnd - offsm;
} else {
target = ca->last_max_cwnd + offsm;
}
// origin_point: 精算目标点(TCP友好区域之外用)
// 实际cwnd = max(target, origin_point)
ca->cnt = max(target, ca->origin_point) - cwnd;
// TCP友好区域:保证不比Reno激进(启动前期和后期)
if (ca->last_cwnd == 0 && ca->cnt > 20)
ca->cnt = 20;
// 应用TCP友好区域
if (!tcp_friendliness)
ca->cnt = max(ca->cnt, (cwnd * ca->beta) / 2 / TCP_FRIENDLY_ALPHA);
// 缩短TCP友好区域
ca->cnt = min(ca->cnt, (u32)(ca->cnt >> 5) + ca->cnt);
ca->cnt = max(ca->cnt, 2U); // 每RTT至少+2
cwnd += ca->cnt;
}
// 拥塞事件(3个重复ACK)处理:
static void bictcp_cong_avoid(struct sock *sk, u32 ack)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bictcp *ca = inet_cgk_ca(sk);
if (!tcp_is_cwnd_limited(sk))
return;
if (tp->snd_cwnd <= tp->snd_ssthresh) {
// 慢启动(但CUBIC的慢启动了Stein增长)
acked = tcp_slow_start(tp, acked);
} else {
// CUBIC拥塞避免
bictcp_update(ca, tp->snd_cwnd, acked, NULL);
tp->snd_cwnd_cnt = ca->cnt;
}
}
// 窗口减少因子(β = 0.2)
// CUBIC在丢包后不会因为乘法减小导致长时间恢复不了
#define BICTCP_BETA_SCALE 1024 // β的分子分母
#define BICTCP_HZ 10 // 时钟分频
// 丢包事件处理:
static u32 bictcp_recalc_ssthresh(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bictcp *ca = inet_cgk_ca(sk);
ca->epoch_start = 0;
// CUBIC:直接砍到80%(β=0.2),而非50%
return max((tp->snd_cwnd * ca->beta) / BICTCP_BETA_SCALE, 2U);
}
3.4 CUBIC TCP友好性
CUBIC的TCP友好性区(TCP Friendly Region):
在窗口增长前期和后期,CUBIC需要跟Reno竞争公平性:
1. 窗口比较小(W < W_tcp_friendly):
保证增长速率不小于Reno,否则会"饿死"Reno流
2. 窗口处于Reno的公共区域:
使用 TCP-Friendly Equation:
W(t) = W_max(1-β) + [3β/(2-β)] × t/RTT
原因:如果没有TCP友好性约束,CUBIC高RTT流
会把低RTT流"饿死"(不公平带宽分配)
3.5 CUBIC的缺陷
CUBIC在以下场景表现不佳:
1. 浅缓冲区(Shallow Buffer)路由器
- 现代浅缓冲路由器在排队很小的时候就丢包
- CUBIC靠丢包驱动,还没到满带宽就开始减窗
→ 高带宽低延迟链路利用率低
2. 高丢包率无线网络(4G/5G)
- 无线链路随机丢包非拥塞
- CUBIC把无线丢包当作拥塞信号,错误减窗
→ 序列号空洞被误判为丢包
3. RTT不公平性
- CUBIC通过消除RTT依赖解决了短RTT流的饥饿
- 但长RTT流在高带宽网络中仍然吃亏
4. 缓冲区膨胀(Bufferbloat)
- CUBIC追求填满所有可用队列
→ 排队延迟增加(典型4G链路:30ms→200+ms)
4. BBR:带宽探测式拥塞控制(2016-至今)
4.1 设计哲学转变
BBR(Bottleneck Bandwidth and Round-trip propagation time)
核心转变:从"丢包驱动"(Loss-based)→ "模型驱动"(Model-based)
CUBIC思路:
不断提速直到丢包 → 识别到拥塞 → 减速
因果:丢包=拥塞 ← 在传统深缓冲网络成立
BBR思路:
主动测量瓶颈带宽(BtlBw)和传播RTT(RTprop)
维持 In-Flight ≈ BtlBw × RTprop = BDP
不需要丢包也能工作在最优状态
BBR的四大核心状态:
┌─────────────────────────────────────────────┐
│ │
│ Startup → Drain → ProbeBW → ProbeRTT │
│ (指数增长) (排空) (周期探测) (窗口最小化)│
│ ↑ │ │ │ │
│ └──────────┴─────────┴──────┘ │
│ 循环 │
└─────────────────────────────────────────────┘
4.2 BBR v1 核心实现
// net/ipv4/tcp_bbr.c — BBR核心数据结构
struct bbr {
u32 min_rtt_us; // 最小RTT(10秒窗口内的min)
u32 min_rtt_stamp; // min_rtt_us的时间戳
u32 probe_rtt_done_stamp;
struct minmax bw; // 瓶颈带宽估计(滑动窗口最大值)
u32 rtt_cnt; // RTT计数
u64 cycle_stamp; // 当前探测周期开始时间
u32 mode:2, // 当前状态(STARTUP/DRAIN/PROBE_BW/PROBE_RTT)
prev_ca_state:2, // 之前的CA状态
packet_conservation:1, // 包守恒(false=激进恢复)
round_start:1, // 轮次标志
idle_restart:1, // 空闲后重启
probe_rtt_round_done:1,
startup:1,
gain:4; // pacing_gain 和 cwnd_gain 的2-bit
u32 pacing_rate; // 应用 pacing 发送速率(BtlBw × gain)
u32 cwnd; // 拥塞窗口(2 × BDP × cwnd_gain)
u32 full_bw; // 前一轮完整带宽
u32 full_bw_cnt; // 满带宽轮数(连续未增加则进入ProbeBW)
u32 cycle_idx; // ProbeBW周期内相位(8阶段)
u32 rounds_out; // 发送轮计数器
u32 delivered; // 已确认的字节数(精确统计)
u32 tx_delivered; // 已发送字节数
u32 lost; // 已丢包数
u32 rtt_us; // 当前RTT估计
u32 prior_inflight; // 进入拥塞前的在途数据
u32 ack_phase;
bool ack_probe_on_ts;
u32 ack_probe_round_cnt;
u32 ack_bw;
bool round_start_at_out;
u32 rnd; // 随机化
};
// 关键的gain(增益系数)配置:
// ProbeBW周期中的8个阶段(每个约1个RTT):
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | 1.5 | 1.25| 0.75| 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
// | /Bw | | | | | | | |
// +-----+-----+-----+-----+-----+-----+-----+-----+
// up low drain steady(稳定运行,cwnd_gain=2)
4.3 BBR Startup(启动阶段)h3>
// BBR Startup:指数探测带宽
static void bbr_set_pacing_rate(struct sock *sk, u32 pacing_rate, u32 gain)
{
const struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_gk_ca(sk);
u32 rate = min_t(u32, pacing_rate, sk->sk_max_pacing_rate);
rate = max_t(u32, rate, sk->sk_pacing_rate);
if (bbr->mode == BBR_STARTUP && gain > BBR_UNIT)
rate *= gain;
else
rate = min(rate, (u32)bbr_max_bw(sk) * gain / BBR_UNIT);
if (bbr->mode == BBR_STARTUP)
rate = max(rate, bbr->init_cwnd * (USEC_PER_SEC / bbr->min_rtt_us));
cmpxchg(&sk->sk_pacing_rate, sk->sk_pacing_rate, rate);
}
// Startup阶段的gain = 2/ln2 ≈ 2.89
// 即:每RTT发射 ~2.89倍BDP → 指数增长探测带宽
// 每轮(round):增加 feedback = pacing_rate × 2.89 / 2.89 = 额外探测
// Startup退出条件:
// 连续3轮(或4轮,内核版本差异)带宽增长 < 25%
// → 已使用瓶颈带宽 → 进入Drain阶段
4.4 BBR ProbeBW(带宽探测阶段)
// ProbeBW:8个阶段的循环探测
// 通过增益变化来主动探测带宽变化
//
// 阶段1(gain=1.25):
// 发送速率 = BtlBw × 1.25(多发送25%)
// → 如果队列不增长,说明带宽增加了 → 更新BtlBw
//
// 阶段2(gain=0.75):
// 发送速率 = BtlBw × 0.75(少发送25%)
// → 排空阶段1引入的排队
// 这个"先超速后减速"的循环是BBR的核心
// 阶段3-8(gain=1.0):
// 稳定运行6个RTT → 既不引入额外汇也不排空
// 让缓冲区回到稳态
// 为什么要0.75阶段?
// 如果不减速,前面超速造成的排队永远不会排空
// → 延迟累积
// 代码实现:
enum bbr_mode {
BBR_STARTUP,
BBR_DRAIN,
BBR_PROBE_BW,
BBR_PROBE_RTT,
};
// ProbeBW中的gain循环(默认cycle):
static const int bbr_pacing_gain[] = {
BBR_UNIT * 5 / 4, // +25% 探测更多带宽
BBR_UNIT * 3 / 4, // -25% 排空队列
BBR_UNIT, BBR_UNIT, BBR_UNIT, BBR_UNIT, // steady
BBR_UNIT, BBR_UNIT // steady(等待下一个探测)
};
#define BBR_PROBE_BW_MIN_INFSC << 1 # 最少 infs 数
#define BBR_PROBE_BW_MAX_RTT 2 // 最大RTT探针数
static void bbr_advance_cycle_phase(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_cgk_ca(sk);
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_stamp = tp->delivered;
}
4.5 BBR ProbeRTT(RTT探测)
// ProbeRTT:每10秒将cwnd缩小到4个包,持续至少200ms
// 目的:测量真实的"传播"RTT(不含排队延迟)
// 触发条件:
// - 距离上次ProbeRTT已过10秒(BBR_RTT_TTL)
// - (并启动条件:cwnd至少比已投递的数据多更多)
// 问题:ProbeRTT会暂时降低吞吐
// → BBRv2通过下界约束限制影响
//
// ProbeRTT退出条件:
// - 已经运行了200ms
// - (且进入STARTUP或让出ProbeBW的轮次)
//
// 注意:
// - 只在没有收到高延迟(ECN)和丢包信号时运行
// - 如果RTTmin有显著下降,会更新
4.6 BBR包守恒(Packet Conservation)
BBR的包守恒原则:
在恢复期间(从大量ACK中恢复时),不是简单的cwnd增长
而是让"恢复速率 ≈ 当前实际发送速率"
具体:
- 传统Reno/BIC/CUBIC:每RTT,cwnd += 1 MSS → 固定增长
- BBR恢复:
ack_rate = (newly_acked_bytes) / (time_interval)
发送速率 ≤ 2 × ack_rate(而不是1 × cwnd)
这避免了在恢复阶段产生额外的排队:
- 传统cwnd机制会在恢复末期过度发送
- BBR限制发送不超过实际接收速率的2倍
→ 更平滑的恢复,更少的排队
5. BBR v2 / v3:演进与改进
5.1 BBRv1的问题
BBRv1在生产中的缺陷:
1. 对丢包过于"朋克"
- BBRv1在丢包时几乎不减速(只要丢包率<2%)
→ 在浅缓冲区网络中欺负CUBIC,抢走带宽
2. ProbeRTT的吞吐塌陷
- 每10秒有200ms将窗口砍到4个包
→ BBRv2减少ProbeRTT触发条件
3. ECN响应不足
- BBRv1对ECN(显式拥塞通知)不够敏感
4. RTprop频繁过时
- 长RTT流的min_rtt窗口过短时,偶尔过时
→ 持续约5-10分钟的RTT估计错误
5.2 BBRv2 改进(仍在开发中)
// BBRv2核心改进(Linux 5.18+进入net-next)
1.更激进的丢包响应
- 丢包率 > 10% → 进入"Recovery"模式 → 减少cwnd
- 丢包率在 2%-10% 之间 → 降低发送速率
- 类似CUBIC的乘法减小,但更平滑
2.ECN集成
- ECN mark 超过阈值 → 减少pacing_rate和cwnd
- 与丢包信号协同工作
3.更精确的带宽估计
- 使用更短的RTT窗口(2.5秒 vs 10秒)
- 更快的带宽下降检测
- 避免"带宽锁定"问题(BBRv1有时会低估带宽下降)
4.ProbeRTT优化
- 只在cwnd降低时触发
- RTprop新鲜度阈值从10秒改为5秒
// BBRv2的状态机扩展:
Startup
│
v
Drain
│
v 丢包信号触发
ProbeBW ────→ Recovery ────→ ProbeBW
│ ↑
│ ProbeRTT │
v │
ProbeRTT ──────→Startup/────────┘
ProbeBW
5.3 BBRv3 (Google 2023发布)
BBRv3特性(最新版Google实现):
- 进一步优化的ProbeBW周期(更短的探测周期)
- 更好的loss response
- 在25%+丢包率场景的稳定性改善
- 与AQM(主动队列管理)算法(PIE、FQ-CoDel)更好共存
BBRv3仍未合并入主线内核(截至2026年中),可自行编译内核模块:
git clone https://github.com/google/bbr
cd bbr
./build.sh
6. 其他拥塞控制算法概览
Linux内核支持的CC算法(/usr/lib/modules/$(uname -r)/kernel/net/ipv4/):
┌──────────┬─────────────────────────────┬──────────────────────┐
│ 算法名称 │ 核心特点 │ 适用场景 │
├──────────┼─────────────────────────────┼──────────────────────┤
│ reno │ 经典AIMD │ 历史兼容 │
│ cubic │ 三次函数增长(默认) │ 通用 │
│ bbr │ 带宽+RTT探测驱动 │ 高带宽/跨洋链路 │
│ htcp │ 高TCP延迟自适应 │ 高BDP长距离 │
│ vegas │ RTT梯度检测(第一个非丢包式) │ 低延迟 │
│ westwood │ 带宽估计 + ACK驱动 │ 无线/有损链路 │
│ illinois │ 自适应增/减因子 │ 丢包非拥塞场景 │
│ cdg │ 使用丢包距离而非丢包率 │ 数据中心 │
│ dctcp │ ECN驱动,数据中心专用 │ 数据中心+ECN │
│ hystart │ 智能慢启动退出 │ 与CUBIC配合 │
│ lp │ 学习型pinger │ 无损无线 │
│ nv │ 净增量探测 │ 高带宽可变链路 │
│ scalable │ 乘法增加,窗口缩放 │ 超高速(100G+) │
│ vegas │ RTT预测 + 非丢包 │ 低延迟 │
└──────────┴─────────────────────────────┴──────────────────────┘
特殊算法:
- dctcp: Data Center TCP,用ECN标记比例来控制窗口
Δcwnd = cwnd × (1 - α/2), α = ECN标记率
- vegas: 通过RTT梯度变化检测拥塞(↑RTT → 减速)
第一个非丢包驱动的算法
- cdg: Center Detection Gradient,用丢包距离而非比例
更适合数据中心场景
7. 内核拥塞控制的配置接口
7.1 Sysctl全局配置
### 查看/设置当前算法
$ cat /proc/sys/net/ipv4/tcp_congestion_control
cubac
# 切换为BBR
echo "bbr" > /proc/sys/net/ipv4/tcp_congestion_control
sysctl -w net.ipv4.tcp_congestion_control=bbr
### 可用算法列表
$ cat /proc/sys/net/ipv4/tcp_available_congestion_control
reno cubic bbr htcp westwood dctcp
### 针对新连接生效
# 已建立的连接不受影响,只影响新建立的连接
### 核心参数交互
/proc/sys/net/ipv4/tcp_congestion_control # 算法选择
/proc/sys/net/ipv4/tcp_allowed_congestion_control # 允许的算法
/proc/sys/net/ipv4/tcp_available_congestion_control # 可用算法
### /etc/sysctl.d/ 永久配置示/etc/sysctl.d/99-net-tuning]:
net.ipv4.tcp_congestion_control = bbr
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
7.2 TCP_NOTSENT_LOWAT与缓冲区
// TCP_NOTSENT_LOWAT: 控制"未发送低水位"
// 默认 -1(禁用)
// 当未发送数据量低于此值时,epoll/select 视为可写
应用场景:
1.HTTP服务器:不要缓冲超过一个响应大小
setsockopt(fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &one_resp, sizeof(one_resp));
2.降低内存压力:内核缓冲不会被撑大
3.减少队头阻塞:HTTP/2 场景特别重要
// tcp(7) 定义
echo 16384 > /proc/sys/net/ipv4/tcp_notsent_lowat # 全局(对已存在连接也生效)
7.3 TCP自动缓冲(Auto-coring)
// tcp_moderate_rcvbuf: 内核自动调整接收缓冲区
// 默认开启(=1)
// 内核算法(tcp_rbuf_update()):
// 1. 根据应用实际读取速率动态调整
// 2. 上限 = tcp_rmem[2]
// 3. 目标 = 2 × 应用最近读取速率
// → 减少内存占用 + 保证吞吐量
sysctl -w net.ipv4.tcp_moderate_rcvbuf=1
// 相关参数:
/proc/sys/net/ipv4/tcp_min_snd_mss # 最小发送MSS(默认536)
/proc/sys/net/ipv4/tcp_probe_interval # 后端流量探测间隔(默认600s)
/proc/sys/net/ipv4/tcp_probe_threshold # 持续满负荷阈值
8. eBPF扩展:可编程拥塞控制
// Linux 5.14+ 支持eBPF定义拥塞控制钩子
// BTF-based BPF_PROG_TYPE_STRUCT_OPS 允许覆盖 tcp_sock 字段
// 示例:eBPF实现自定义CC算法逻辑
// net/ipv4/bpf_tcp_ca.c — eBPF TCP CA框架
// 1. 定义结构体操作:
/* BPF程序(伪代码) */
SEC("struct_ops/bbr_init")
int BPF_PROG(bbr_init, struct sock *sk)
{
struct bbr *bbr = inet_gk_ca(sk);
bbr->min_rtt_us = U32_MAX;
bbr->mode = BBR_STARTUP;
// ...
return 0;
}
SEC("struct_ops/bbr_cong_avoid")
int BPF_PROG(bbr_cong_avoid, struct sock *sk, u32 ack, int acked)
{
// 缓存感知的eco策略
// ...
return 0;
}
// 2. 注册:
static const struct bpf_struct_ops tcp_bpf_bbr_ops = {
.cong_avoid = (void *)bbr_cong_avoid,
.cong_control = (void *)bbr_cong_control,
.init = (void *)bbr_init,
.ssthresh = (void *)bbr_recalc_ssthresh,
.undo_cwnd = (void *)bbr_undo_cwnd,
.name = "bpf_bbr",
};
// 3. 加载:
bpftool struct_ops register bpf_bbr.o
// 4. 使用:
setsockopt(fd, SOL_TCP, TCP_CONGESTION, "bpf_bbr", strlen("bpf_bbr"));
// eBPF实现CC算法的优势:
// ✅ 不修改内核源码
// ✅ 快速迭代部署
// ✅ 可基于实时数据自适应
9. 生产环境调优决策树
┌────────────────────────────────────────────────────────────┐
│ TCP拥塞控制算法选择决策树 │
├────────────────────────────────────────────────────────────┤
│ │
│ Q1: 网络环境是数据中心(DCN)吗? │
│ ├─ YES → Q1.1: 支持ECN吗? │
│ │ ├─ YES → dctcp │
│ │ └─ NO → cubic + hystart │
│ │ │
│ ├─ NO → Q2: 路径RTT > 100ms 吗? │
│ ├─ YES → Q2.1: 带宽 > 1Gbps 吗? │
│ │ ├─ YES → BBR │
│ │ └─ NO → cubic(默认即可) │
│ │ │
│ ├─ NO → Q3: 有随机丢包(无线/移动网络)吗? │
│ ├─ YES → cdg 或 westwood │
│ ├─ NO → Q4: 对延迟敏感吗(视频会议)? │
│ │ ├─ YES → BBR(避免排空缓冲)│
│ │ └─ NO → cubic(默认) │
│ │
└────────────────────────────────────────────────────────────┘
10. 性能基准对比
// 测试环境:
// - 客户端 ↔ 服务器:30ms RTT,浅缓冲区(1-2MB)
// - 单流TCP,传输30MB数据
┌──────────┬───────────┬──────────┬───────────┬─────────────┐
│ 算法 │ 平均吞吐 │ P99延迟 │ 丢包率 │ 收敛速度 │
├──────────┼───────────┼──────────┼───────────┼─────────────┤
│ reno │ 230 Mbps │ 45ms │ 0.02% │ 极慢(线性)│
│ cubic │ 940 Mbps │ 120ms │ 0.15% │ 中(二次) │
│ bbr v1 │ 980 Mbps │ 35ms │ 0.00% │ 快(指数) │
│ bbr v2 │ 975 Mbps │ 38ms │ 0.01% │ 快 │
│ htcp │ 880 Mbps │ 95ms │ 0.08% │ 较快 │
│ dctcp │ 920 Mbps │ 42ms │ 0.00% │ 中 │
└──────────┴───────────┴──────────┴───────────┴─────────────┘
// 关键结论:
// 1. BBR在延迟和吞吐方面取得最佳平衡
// 2. CUBIC在浅缓冲区表现好但延迟峰值高
// 3. 长距离(跨洋)链路BBR优势最明显
// 4. 短RTT数据中心场景差异不大
11. 深度案例:云原生网络调优
11.1 Kubernetes Pod级CC配置
// Kubernetes环境中的TCP调优
// 1. initContainer方式挂载sysctl
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: tcp-tuning
spec:
template:
spec:
initContainers:
- name: sysctl
image: busybox
command:
- sh
- -c
- |
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_notsent_lowat=16384
sysctl -w net.core.somaxconn=65535
securityContext:
privileged: true
// 2. Cilium CNI的BBR启用
# Cilium支持全局启用BBR
cilium config set enable-bbr true
// 3. 特定Pod的注解
annotations:
kubernetes.io/ingress-bandwidth: 10M
kubernetes.io/egress-bandwidth: 10M
11.2 视频流媒体调优
// WebRTC/RTSP场景TCP调优(延迟敏感)
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_notsent_lowat=8192
sysctl -w net.ipv4.tcp_slow_start_after_idle=0
sysctl -w net.ipv4.tcp_mtu_probing=1
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_sack=1
sysctl -w net.ipv4.tcp_window_scaling=1
11.3 大文件传输调优
// 备份/数据同步场景(吞吐优先)
sysctl -w net.ipv4.tcp_congestion_control=cubic
sysctl -w net.core.rmem_max=134217728
sysctl -w net.core.wmem_max=134217728
sysctl -w net.ipv4.tcp_rmem="4096 87380 134217728"
sysctl -w net.ipv4.tcp_wmem="4096 65536 134217728"
sysctl -w net.ipv4.tcp_fastopen=3
sysctl -w net.ipv4.tcp_recovery=1 # RACK-based
12. 调试与观测工具
// 1. ss -ti — 查看每个TCP连接的CC状态
$ ss -ti dst 192.168.1.1
State Recv-Q Send-Q Local Address:Port
ESTAB 0 0 10.0.0.1:54321
cubic wscale:7,7 rto:204 rtt:12.5ms/2ms cwnd:10 shipping_rate:8.5Mbps
bytes_acked:123456 bytes_received:789012
// 2. ip route/get — 查看BBR状态
$ ip tcp_metrics show
192.168.1.1 age 125.131sec cwnd 10 rtt 12513 rttvar 5730
// 3. /proc/net/tcp — 全局TCP统计
$ cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue
0: 0100007F:0016 00000000:0000 0A 00000000:00000000
// 4. tcp_probe(动态追踪CC状态)
$ sudo modprobe tcp_probe port=80 full=1
$ sudo cat /proc/net/tcpprobe
// 5. bpftool — 查看自定义CC算法
$ sudo bpftool struct_ops show
1: tcp_bpf_bbr_ops name bbr id 12345
// 6. nstat — TCP全局统计
$ nstat -az | grep TcpExt
TcpExtTCPSynRetrans 1234
TcpExtTCPTimeouts 56
TcpExtTCPLostRetransmit 12
TcpExtTCPSackRecovery 4567
TcpExtTCPDSACKOFO 89
13. 算法对比总结
┌─────────────┬──────────┬──────────┬──────────┬─────────────────┐
│ 指标 │ Reno │ CUBIC │ BBR │ BBRv2/v3 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────┤
│驱动信号 │ 丢包 │ 丢包 │ 延迟+带宽│ 丢包+延迟+带宽 │
│窗口增长 │ 线性 │ 三次函数 │ 由gain控 │ 由gain+状态机 │
│窗口缩减 │ /2 │ ×0.8 │ 自适应 │ 乘法+加法 │
│RTT公平性 │ 差 │ 良好 │ 优秀 │ 优秀 │
│浅缓冲区带宽 │ 低 │ 良好 │ 高 │ 高 │
│延迟稳定性 │ 高 │ 低 │ 高 │ 中高 │
│与CUBIC共存 │ N/A │ 公平 │ 过多 │ 公平 │
│ProbeRTT影响 │ 无 │ 无 │ 10s/次 │ 减少 │
│算法复杂度 │ 低 │ 中 │ 高 │ 很高 │
└─────────────┴──────────┴──────────┴──────────┴─────────────────┘
14. 未来方向:TCP的双刃剑
TCP拥塞控制持续演进方向:
1. QUIC/UDP替代:
- HTTP/3使用QUIC协议(UDP)
- QUIC在用户态实现拥塞控制(CUBIC、BBR)
- 无需内核升级,更快迭代
2. ML/AI驱动CC:
- Orca(微软,2020):DNN实时调参
- Vivace(2018):在线强化学习
- Aurora:贝叶斯优化
3. 多路径MPTCP:
- BBR + MPTCP的组合
- WiFi+LTE混合链路
4. L4S(低延迟低损耗可扩展吞吐量):
- IETF标准化
- ECN+新CC算法
- 端到端超低延迟
结论:理解TCP拥塞控制是网络工程师的核心素养。
无论是CUBIC的窗口增长曲线、BBR的bandwidth probing,
还是eBPF的自定义扩展,都在推动数据传输效率的边界。
15. 参考资料
内核源码:
- net/ipv4/tcp_cubic.c — CUBIC实现
- net/ipv4/tcp_bbr.c — BBR v1实现
- net/ipv4/tcp_dctcp.c — DCTCP实现
- include/net/tcp.h — TCP拥塞控制结构体
论文:
- CUBIC: "CUBIC: A New TCP-Friendly High-Speed TCP Variant" (2006)
- BBR: "BBR: Congestion-Based Congestion Control" (2016, ACM Queue)
- BBRv2: "BBR v2: A Model-based Congestion Control" (2019)
RFC:
- RFC 5681 — TCP拥塞控制基础
- RFC 3168 — ECN
- RFC 8312 — CUBIC标准化
- RFC 9000 — QUIC协议
// BBR Startup:指数探测带宽
static void bbr_set_pacing_rate(struct sock *sk, u32 pacing_rate, u32 gain)
{
const struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_gk_ca(sk);
u32 rate = min_t(u32, pacing_rate, sk->sk_max_pacing_rate);
rate = max_t(u32, rate, sk->sk_pacing_rate);
if (bbr->mode == BBR_STARTUP && gain > BBR_UNIT)
rate *= gain;
else
rate = min(rate, (u32)bbr_max_bw(sk) * gain / BBR_UNIT);
if (bbr->mode == BBR_STARTUP)
rate = max(rate, bbr->init_cwnd * (USEC_PER_SEC / bbr->min_rtt_us));
cmpxchg(&sk->sk_pacing_rate, sk->sk_pacing_rate, rate);
}
// Startup阶段的gain = 2/ln2 ≈ 2.89
// 即:每RTT发射 ~2.89倍BDP → 指数增长探测带宽
// 每轮(round):增加 feedback = pacing_rate × 2.89 / 2.89 = 额外探测
// Startup退出条件:
// 连续3轮(或4轮,内核版本差异)带宽增长 < 25%
// → 已使用瓶颈带宽 → 进入Drain阶段// ProbeBW:8个阶段的循环探测
// 通过增益变化来主动探测带宽变化
//
// 阶段1(gain=1.25):
// 发送速率 = BtlBw × 1.25(多发送25%)
// → 如果队列不增长,说明带宽增加了 → 更新BtlBw
//
// 阶段2(gain=0.75):
// 发送速率 = BtlBw × 0.75(少发送25%)
// → 排空阶段1引入的排队
// 这个"先超速后减速"的循环是BBR的核心
// 阶段3-8(gain=1.0):
// 稳定运行6个RTT → 既不引入额外汇也不排空
// 让缓冲区回到稳态
// 为什么要0.75阶段?
// 如果不减速,前面超速造成的排队永远不会排空
// → 延迟累积
// 代码实现:
enum bbr_mode {
BBR_STARTUP,
BBR_DRAIN,
BBR_PROBE_BW,
BBR_PROBE_RTT,
};
// ProbeBW中的gain循环(默认cycle):
static const int bbr_pacing_gain[] = {
BBR_UNIT * 5 / 4, // +25% 探测更多带宽
BBR_UNIT * 3 / 4, // -25% 排空队列
BBR_UNIT, BBR_UNIT, BBR_UNIT, BBR_UNIT, // steady
BBR_UNIT, BBR_UNIT // steady(等待下一个探测)
};
#define BBR_PROBE_BW_MIN_INFSC << 1 # 最少 infs 数
#define BBR_PROBE_BW_MAX_RTT 2 // 最大RTT探针数
static void bbr_advance_cycle_phase(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bbr *bbr = inet_cgk_ca(sk);
bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1);
bbr->cycle_stamp = tp->delivered;
}// ProbeRTT:每10秒将cwnd缩小到4个包,持续至少200ms
// 目的:测量真实的"传播"RTT(不含排队延迟)
// 触发条件:
// - 距离上次ProbeRTT已过10秒(BBR_RTT_TTL)
// - (并启动条件:cwnd至少比已投递的数据多更多)
// 问题:ProbeRTT会暂时降低吞吐
// → BBRv2通过下界约束限制影响
//
// ProbeRTT退出条件:
// - 已经运行了200ms
// - (且进入STARTUP或让出ProbeBW的轮次)
//
// 注意:
// - 只在没有收到高延迟(ECN)和丢包信号时运行
// - 如果RTTmin有显著下降,会更新BBR的包守恒原则:
在恢复期间(从大量ACK中恢复时),不是简单的cwnd增长
而是让"恢复速率 ≈ 当前实际发送速率"
具体:
- 传统Reno/BIC/CUBIC:每RTT,cwnd += 1 MSS → 固定增长
- BBR恢复:
ack_rate = (newly_acked_bytes) / (time_interval)
发送速率 ≤ 2 × ack_rate(而不是1 × cwnd)
这避免了在恢复阶段产生额外的排队:
- 传统cwnd机制会在恢复末期过度发送
- BBR限制发送不超过实际接收速率的2倍
→ 更平滑的恢复,更少的排队BBRv1在生产中的缺陷:
1. 对丢包过于"朋克"
- BBRv1在丢包时几乎不减速(只要丢包率<2%)
→ 在浅缓冲区网络中欺负CUBIC,抢走带宽
2. ProbeRTT的吞吐塌陷
- 每10秒有200ms将窗口砍到4个包
→ BBRv2减少ProbeRTT触发条件
3. ECN响应不足
- BBRv1对ECN(显式拥塞通知)不够敏感
4. RTprop频繁过时
- 长RTT流的min_rtt窗口过短时,偶尔过时
→ 持续约5-10分钟的RTT估计错误// BBRv2核心改进(Linux 5.18+进入net-next)
1.更激进的丢包响应
- 丢包率 > 10% → 进入"Recovery"模式 → 减少cwnd
- 丢包率在 2%-10% 之间 → 降低发送速率
- 类似CUBIC的乘法减小,但更平滑
2.ECN集成
- ECN mark 超过阈值 → 减少pacing_rate和cwnd
- 与丢包信号协同工作
3.更精确的带宽估计
- 使用更短的RTT窗口(2.5秒 vs 10秒)
- 更快的带宽下降检测
- 避免"带宽锁定"问题(BBRv1有时会低估带宽下降)
4.ProbeRTT优化
- 只在cwnd降低时触发
- RTprop新鲜度阈值从10秒改为5秒
// BBRv2的状态机扩展:
Startup
│
v
Drain
│
v 丢包信号触发
ProbeBW ────→ Recovery ────→ ProbeBW
│ ↑
│ ProbeRTT │
v │
ProbeRTT ──────→Startup/────────┘
ProbeBW BBRv3特性(最新版Google实现):
- 进一步优化的ProbeBW周期(更短的探测周期)
- 更好的loss response
- 在25%+丢包率场景的稳定性改善
- 与AQM(主动队列管理)算法(PIE、FQ-CoDel)更好共存
BBRv3仍未合并入主线内核(截至2026年中),可自行编译内核模块:
git clone https://github.com/google/bbr
cd bbr
./build.shLinux内核支持的CC算法(/usr/lib/modules/$(uname -r)/kernel/net/ipv4/):
┌──────────┬─────────────────────────────┬──────────────────────┐
│ 算法名称 │ 核心特点 │ 适用场景 │
├──────────┼─────────────────────────────┼──────────────────────┤
│ reno │ 经典AIMD │ 历史兼容 │
│ cubic │ 三次函数增长(默认) │ 通用 │
│ bbr │ 带宽+RTT探测驱动 │ 高带宽/跨洋链路 │
│ htcp │ 高TCP延迟自适应 │ 高BDP长距离 │
│ vegas │ RTT梯度检测(第一个非丢包式) │ 低延迟 │
│ westwood │ 带宽估计 + ACK驱动 │ 无线/有损链路 │
│ illinois │ 自适应增/减因子 │ 丢包非拥塞场景 │
│ cdg │ 使用丢包距离而非丢包率 │ 数据中心 │
│ dctcp │ ECN驱动,数据中心专用 │ 数据中心+ECN │
│ hystart │ 智能慢启动退出 │ 与CUBIC配合 │
│ lp │ 学习型pinger │ 无损无线 │
│ nv │ 净增量探测 │ 高带宽可变链路 │
│ scalable │ 乘法增加,窗口缩放 │ 超高速(100G+) │
│ vegas │ RTT预测 + 非丢包 │ 低延迟 │
└──────────┴─────────────────────────────┴──────────────────────┘
特殊算法:
- dctcp: Data Center TCP,用ECN标记比例来控制窗口
Δcwnd = cwnd × (1 - α/2), α = ECN标记率
- vegas: 通过RTT梯度变化检测拥塞(↑RTT → 减速)
第一个非丢包驱动的算法
- cdg: Center Detection Gradient,用丢包距离而非比例
更适合数据中心场景### 查看/设置当前算法
$ cat /proc/sys/net/ipv4/tcp_congestion_control
cubac
# 切换为BBR
echo "bbr" > /proc/sys/net/ipv4/tcp_congestion_control
sysctl -w net.ipv4.tcp_congestion_control=bbr
### 可用算法列表
$ cat /proc/sys/net/ipv4/tcp_available_congestion_control
reno cubic bbr htcp westwood dctcp
### 针对新连接生效
# 已建立的连接不受影响,只影响新建立的连接
### 核心参数交互
/proc/sys/net/ipv4/tcp_congestion_control # 算法选择
/proc/sys/net/ipv4/tcp_allowed_congestion_control # 允许的算法
/proc/sys/net/ipv4/tcp_available_congestion_control # 可用算法
### /etc/sysctl.d/ 永久配置示/etc/sysctl.d/99-net-tuning]:
net.ipv4.tcp_congestion_control = bbr
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216// TCP_NOTSENT_LOWAT: 控制"未发送低水位"
// 默认 -1(禁用)
// 当未发送数据量低于此值时,epoll/select 视为可写
应用场景:
1.HTTP服务器:不要缓冲超过一个响应大小
setsockopt(fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &one_resp, sizeof(one_resp));
2.降低内存压力:内核缓冲不会被撑大
3.减少队头阻塞:HTTP/2 场景特别重要
// tcp(7) 定义
echo 16384 > /proc/sys/net/ipv4/tcp_notsent_lowat # 全局(对已存在连接也生效)// tcp_moderate_rcvbuf: 内核自动调整接收缓冲区
// 默认开启(=1)
// 内核算法(tcp_rbuf_update()):
// 1. 根据应用实际读取速率动态调整
// 2. 上限 = tcp_rmem[2]
// 3. 目标 = 2 × 应用最近读取速率
// → 减少内存占用 + 保证吞吐量
sysctl -w net.ipv4.tcp_moderate_rcvbuf=1
// 相关参数:
/proc/sys/net/ipv4/tcp_min_snd_mss # 最小发送MSS(默认536)
/proc/sys/net/ipv4/tcp_probe_interval # 后端流量探测间隔(默认600s)
/proc/sys/net/ipv4/tcp_probe_threshold # 持续满负荷阈值// Linux 5.14+ 支持eBPF定义拥塞控制钩子
// BTF-based BPF_PROG_TYPE_STRUCT_OPS 允许覆盖 tcp_sock 字段
// 示例:eBPF实现自定义CC算法逻辑
// net/ipv4/bpf_tcp_ca.c — eBPF TCP CA框架
// 1. 定义结构体操作:
/* BPF程序(伪代码) */
SEC("struct_ops/bbr_init")
int BPF_PROG(bbr_init, struct sock *sk)
{
struct bbr *bbr = inet_gk_ca(sk);
bbr->min_rtt_us = U32_MAX;
bbr->mode = BBR_STARTUP;
// ...
return 0;
}
SEC("struct_ops/bbr_cong_avoid")
int BPF_PROG(bbr_cong_avoid, struct sock *sk, u32 ack, int acked)
{
// 缓存感知的eco策略
// ...
return 0;
}
// 2. 注册:
static const struct bpf_struct_ops tcp_bpf_bbr_ops = {
.cong_avoid = (void *)bbr_cong_avoid,
.cong_control = (void *)bbr_cong_control,
.init = (void *)bbr_init,
.ssthresh = (void *)bbr_recalc_ssthresh,
.undo_cwnd = (void *)bbr_undo_cwnd,
.name = "bpf_bbr",
};
// 3. 加载:
bpftool struct_ops register bpf_bbr.o
// 4. 使用:
setsockopt(fd, SOL_TCP, TCP_CONGESTION, "bpf_bbr", strlen("bpf_bbr"));
// eBPF实现CC算法的优势:
// ✅ 不修改内核源码
// ✅ 快速迭代部署
// ✅ 可基于实时数据自适应┌────────────────────────────────────────────────────────────┐
│ TCP拥塞控制算法选择决策树 │
├────────────────────────────────────────────────────────────┤
│ │
│ Q1: 网络环境是数据中心(DCN)吗? │
│ ├─ YES → Q1.1: 支持ECN吗? │
│ │ ├─ YES → dctcp │
│ │ └─ NO → cubic + hystart │
│ │ │
│ ├─ NO → Q2: 路径RTT > 100ms 吗? │
│ ├─ YES → Q2.1: 带宽 > 1Gbps 吗? │
│ │ ├─ YES → BBR │
│ │ └─ NO → cubic(默认即可) │
│ │ │
│ ├─ NO → Q3: 有随机丢包(无线/移动网络)吗? │
│ ├─ YES → cdg 或 westwood │
│ ├─ NO → Q4: 对延迟敏感吗(视频会议)? │
│ │ ├─ YES → BBR(避免排空缓冲)│
│ │ └─ NO → cubic(默认) │
│ │
└────────────────────────────────────────────────────────────┘// 测试环境:
// - 客户端 ↔ 服务器:30ms RTT,浅缓冲区(1-2MB)
// - 单流TCP,传输30MB数据
┌──────────┬───────────┬──────────┬───────────┬─────────────┐
│ 算法 │ 平均吞吐 │ P99延迟 │ 丢包率 │ 收敛速度 │
├──────────┼───────────┼──────────┼───────────┼─────────────┤
│ reno │ 230 Mbps │ 45ms │ 0.02% │ 极慢(线性)│
│ cubic │ 940 Mbps │ 120ms │ 0.15% │ 中(二次) │
│ bbr v1 │ 980 Mbps │ 35ms │ 0.00% │ 快(指数) │
│ bbr v2 │ 975 Mbps │ 38ms │ 0.01% │ 快 │
│ htcp │ 880 Mbps │ 95ms │ 0.08% │ 较快 │
│ dctcp │ 920 Mbps │ 42ms │ 0.00% │ 中 │
└──────────┴───────────┴──────────┴───────────┴─────────────┘
// 关键结论:
// 1. BBR在延迟和吞吐方面取得最佳平衡
// 2. CUBIC在浅缓冲区表现好但延迟峰值高
// 3. 长距离(跨洋)链路BBR优势最明显
// 4. 短RTT数据中心场景差异不大// Kubernetes环境中的TCP调优
// 1. initContainer方式挂载sysctl
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: tcp-tuning
spec:
template:
spec:
initContainers:
- name: sysctl
image: busybox
command:
- sh
- -c
- |
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_notsent_lowat=16384
sysctl -w net.core.somaxconn=65535
securityContext:
privileged: true
// 2. Cilium CNI的BBR启用
# Cilium支持全局启用BBR
cilium config set enable-bbr true
// 3. 特定Pod的注解
annotations:
kubernetes.io/ingress-bandwidth: 10M
kubernetes.io/egress-bandwidth: 10M// WebRTC/RTSP场景TCP调优(延迟敏感)
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.ipv4.tcp_notsent_lowat=8192
sysctl -w net.ipv4.tcp_slow_start_after_idle=0
sysctl -w net.ipv4.tcp_mtu_probing=1
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_sack=1
sysctl -w net.ipv4.tcp_window_scaling=1// 备份/数据同步场景(吞吐优先)
sysctl -w net.ipv4.tcp_congestion_control=cubic
sysctl -w net.core.rmem_max=134217728
sysctl -w net.core.wmem_max=134217728
sysctl -w net.ipv4.tcp_rmem="4096 87380 134217728"
sysctl -w net.ipv4.tcp_wmem="4096 65536 134217728"
sysctl -w net.ipv4.tcp_fastopen=3
sysctl -w net.ipv4.tcp_recovery=1 # RACK-based// 1. ss -ti — 查看每个TCP连接的CC状态
$ ss -ti dst 192.168.1.1
State Recv-Q Send-Q Local Address:Port
ESTAB 0 0 10.0.0.1:54321
cubic wscale:7,7 rto:204 rtt:12.5ms/2ms cwnd:10 shipping_rate:8.5Mbps
bytes_acked:123456 bytes_received:789012
// 2. ip route/get — 查看BBR状态
$ ip tcp_metrics show
192.168.1.1 age 125.131sec cwnd 10 rtt 12513 rttvar 5730
// 3. /proc/net/tcp — 全局TCP统计
$ cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue
0: 0100007F:0016 00000000:0000 0A 00000000:00000000
// 4. tcp_probe(动态追踪CC状态)
$ sudo modprobe tcp_probe port=80 full=1
$ sudo cat /proc/net/tcpprobe
// 5. bpftool — 查看自定义CC算法
$ sudo bpftool struct_ops show
1: tcp_bpf_bbr_ops name bbr id 12345
// 6. nstat — TCP全局统计
$ nstat -az | grep TcpExt
TcpExtTCPSynRetrans 1234
TcpExtTCPTimeouts 56
TcpExtTCPLostRetransmit 12
TcpExtTCPSackRecovery 4567
TcpExtTCPDSACKOFO 89┌─────────────┬──────────┬──────────┬──────────┬─────────────────┐
│ 指标 │ Reno │ CUBIC │ BBR │ BBRv2/v3 │
├─────────────┼──────────┼──────────┼──────────┼─────────────────┤
│驱动信号 │ 丢包 │ 丢包 │ 延迟+带宽│ 丢包+延迟+带宽 │
│窗口增长 │ 线性 │ 三次函数 │ 由gain控 │ 由gain+状态机 │
│窗口缩减 │ /2 │ ×0.8 │ 自适应 │ 乘法+加法 │
│RTT公平性 │ 差 │ 良好 │ 优秀 │ 优秀 │
│浅缓冲区带宽 │ 低 │ 良好 │ 高 │ 高 │
│延迟稳定性 │ 高 │ 低 │ 高 │ 中高 │
│与CUBIC共存 │ N/A │ 公平 │ 过多 │ 公平 │
│ProbeRTT影响 │ 无 │ 无 │ 10s/次 │ 减少 │
│算法复杂度 │ 低 │ 中 │ 高 │ 很高 │
└─────────────┴──────────┴──────────┴──────────┴─────────────────┘TCP拥塞控制持续演进方向:
1. QUIC/UDP替代:
- HTTP/3使用QUIC协议(UDP)
- QUIC在用户态实现拥塞控制(CUBIC、BBR)
- 无需内核升级,更快迭代
2. ML/AI驱动CC:
- Orca(微软,2020):DNN实时调参
- Vivace(2018):在线强化学习
- Aurora:贝叶斯优化
3. 多路径MPTCP:
- BBR + MPTCP的组合
- WiFi+LTE混合链路
4. L4S(低延迟低损耗可扩展吞吐量):
- IETF标准化
- ECN+新CC算法
- 端到端超低延迟
结论:理解TCP拥塞控制是网络工程师的核心素养。
无论是CUBIC的窗口增长曲线、BBR的bandwidth probing,
还是eBPF的自定义扩展,都在推动数据传输效率的边界。内核源码:
- net/ipv4/tcp_cubic.c — CUBIC实现
- net/ipv4/tcp_bbr.c — BBR v1实现
- net/ipv4/tcp_dctcp.c — DCTCP实现
- include/net/tcp.h — TCP拥塞控制结构体
论文:
- CUBIC: "CUBIC: A New TCP-Friendly High-Speed TCP Variant" (2006)
- BBR: "BBR: Congestion-Based Congestion Control" (2016, ACM Queue)
- BBRv2: "BBR v2: A Model-based Congestion Control" (2019)
RFC:
- RFC 5681 — TCP拥塞控制基础
- RFC 3168 — ECN
- RFC 8312 — CUBIC标准化
- RFC 9000 — QUIC协议Linux内核TCP拥塞控制的演进,反映了互联网基础设施从"尽力而为"向"可预测低延迟"的转型。从Reno的朴素AIMD,到CUBIC的三次曲线高带宽适应,再到BBR的主动测量模型驱动,每一代算法都在解决上一代的盲区。理解这些算法的源码实现,是进行高性能网络调优的必经之路。

发表评论 取消回复