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 不是孤立的。我画了一张图帮你理清它们之间的逻辑:

网络时延模型核心概念关系图 RTT(往返时间) 传播 + 处理 + 排队 RTO(重传超时) SRTT + 4×RTTVAR BDP(带宽时延积) 带宽 × RTT RTT 是基础测量值 → RTO 依赖 RTT 的均值和方差 → BDP 直接用 RTT 计算管道容量 拥塞控制算法 RTT 变化 → 判断拥塞 → 调整窗口 自适应码率(ABR) BDP 估算 → 选择合适码率 调试口诀 RTT 测不准 → RTO 算不对 → 重传炸裂 BDP 算不清 → 窗口设太小 → 带宽浪费 三个概念串起来,流控调优才有方向

2.5 实战:如何测量和计算

纸上谈兵没意思。我分享一个实际调试中用到的测量方法:

  1. RTT 测量:用 ping 或 tcpdump 抓包看 SYN→SYN-ACK 的时间差。注意要排除 ICMP 限速的影响,我习惯同时用 TCP 的 timestamp 选项做交叉验证。
  2. RTO 验证:在发送端抓包,看数据包和重传包的时间间隔。如果 RTO 明显小于实际 RTT 的 2 倍,说明算法太激进。
  3. BDP 估算:用 iperf3 测带宽,同时用 ping 测 RTT,然后相乘。但要注意:带宽要取瓶颈链路的值,不是本地网卡的速度。

核心结论:RTT 是基础,RTO 是保护,BDP 是天花板。调流控时,先确认 RTT 测量是否准确,再检查 RTO 是否合理,最后看窗口大小是否匹配 BDP。这三步走完,80% 的性能问题都能定位。