2. 网络时延模型:RTT、RTO、带宽时延积(BDP)的概念与计算
聊到流控和自适应码率,有三个概念你绕不开:RTT、RTO 和 BDP。我见过不少工程师,调了半天拥塞窗口,结果连 RTT 的测量抖动都没搞清楚。说白了,这三个东西就是网络传输的「物理定律」,不理解它们,你写的流控算法就是瞎蒙。
2.1 RTT:往返时间
RTT(Round-Trip Time),就是数据包从发送端出发,到接收端再返回确认(ACK)的总耗时。我习惯把它拆成三段:
- 传播时延:信号在物理链路上跑的时间,取决于距离和介质。光纤里光速约 2×10⁸ m/s,跨洋链路这一项就占大头。
- 处理时延:路由器、交换机查表转发的时间。一般微秒级,但遇到拥塞的节点会暴涨。
- 排队时延:数据包在中间节点缓冲区里等着的时长。这是 RTT 抖动的主要来源,也是拥塞控制要对付的核心对象。
关键认知:RTT 不是固定值。我在项目中遇到过,同一个 CDN 节点,白天 RTT 稳定在 30ms,晚高峰能跳到 200ms+。所以流控算法必须用 SRTT(Smoothed RTT) 做滑动平均,不能拿单次测量值直接算。
RTT 的采样其实挺讲究。TCP 里用 timestamp 选项来算:
// 伪代码:RTT 采样
send_time = get_clock();
// 发送数据包...
// 收到 ACK
rtt_sample = get_clock() - send_time;
// 平滑处理(RFC 6298)
srtt = (1 - α) × srtt + α × rtt_sample; // α 通常取 1/8
嗯,这里要注意:如果发生重传,你收到的 ACK 对应的是原始包还是重传包?这叫「重传歧义问题」。我早期踩过这个坑,后来改用 Karn 算法——重传期间不更新 RTT 样本,等收到下一个正常 ACK 再说。
2.2 RTO:重传超时
RTO(Retransmission Timeout),就是发送端等多久没收到 ACK 就重传。设得太短,网络稍微抖一下就疯狂重传,造成拥塞崩溃;设得太长,丢包后恢复慢,用户体验断崖式下跌。
RFC 6298 给出了经典算法:
// RTO 计算(RFC 6298)
RTO = SRTT + max(G, 4 × RTTVAR)
// 其中:
// SRTT = 平滑后的 RTT
// RTTVAR = RTT 的方差(抖动)
// G = 时钟粒度(通常 1ms 或 10ms)
// 实际实现中,RTO 有上下界:
RTO_min = 200ms // 避免过于激进
RTO_max = 120s // 避免死等
你想想看,为什么 RTO 要加 4 倍的方差?因为网络抖动是常态。我曾经调试一个跨国直播流,RTT 均值 150ms,但方差高达 80ms。如果只用 SRTT 算 RTO,重传率直接飙到 15%。加上 RTTVAR 后,RTO 自动放宽到 470ms,重传率降到 2% 以下。
避坑指南:我曾经在嵌入式设备上把时钟粒度 G 设成 100ms,结果 RTO 计算总是偏大,丢包后恢复慢得像蜗牛。后来改成 10ms 粒度,问题解决。记住:G 不能大于 RTT 的典型值,否则平滑效果会失真。
2.3 带宽时延积(BDP)
BDP(Bandwidth-Delay Product),就是「管道容量」。公式很简单:
BDP = 带宽 × RTT
单位是比特(或字节)。它表示:在数据到达接收端并返回 ACK 之前,发送端最多能往网络里塞多少数据。说白了,BDP 就是你的「飞行中数据」上限。
举个例子:
| 场景 | 带宽 | RTT | BDP | 含义 |
|---|---|---|---|---|
| 家庭宽带 | 100 Mbps | 10 ms | 125 KB | 窗口开到 125KB 就能跑满 |
| 跨国专线 | 100 Mbps | 200 ms | 2.5 MB | 窗口需要 2.5MB 才能跑满 |
| 卫星链路 | 50 Mbps | 600 ms | 3.75 MB | 窗口巨大,传统 TCP 很难填满 |
我当年调一个卫星视频传输系统,BDP 算出来 4MB,但 TCP 拥塞窗口才 64KB。你想想看,链路利用率不到 2%!后来改用窗口缩放选项(Window Scaling)和 BBR 算法,才把吞吐提上去。
实用技巧:我建议你在做流控调优时,先算一下链路的 BDP。如果接收窗口 < BDP,说明瓶颈在接收端;如果拥塞窗口 < BDP,说明拥塞控制太保守。这两个不等式能帮你快速定位问题方向。
2.4 三者的关系与知识体系
RTT、RTO、BDP 不是孤立的。我画了一张图帮你理清它们之间的逻辑:
2.5 实战:如何测量和计算
纸上谈兵没意思。我分享一个实际调试中用到的测量方法:
- RTT 测量:用
ping或tcpdump抓包看 SYN→SYN-ACK 的时间差。注意要排除 ICMP 限速的影响,我习惯同时用 TCP 的 timestamp 选项做交叉验证。 - RTO 验证:在发送端抓包,看数据包和重传包的时间间隔。如果 RTO 明显小于实际 RTT 的 2 倍,说明算法太激进。
- BDP 估算:用
iperf3测带宽,同时用ping测 RTT,然后相乘。但要注意:带宽要取瓶颈链路的值,不是本地网卡的速度。
核心结论:RTT 是基础,RTO 是保护,BDP 是天花板。调流控时,先确认 RTT 测量是否准确,再检查 RTO 是否合理,最后看窗口大小是否匹配 BDP。这三步走完,80% 的性能问题都能定位。