RTT,Round-Trip Time。这三个字母太普通了,以至于大多数工程师只会瞟一眼监控面板上的数字,嗯,30ms,还行。可一旦出了事故——超时风暴、重传率飘红、吞吐量腰斩——你才会意识到,你对它的理解可能只停留在 /proc/sys/net/ipv4/tcp_rtt_default 那个默认值。我曾在深夜被一个诡异的延迟抖动叫醒,抓包、翻内核代码、最终追溯到一次 RTT 采样被延迟 ACK 污染的全过程。那次之后,我再也不敢轻视它。这就是为什么我想把 RTT 的骨头拆开给你看。
EWMA:RTT 估计的数学骨架
TCP 需要一种机制来预测下一个 RTT,以设定合理的重传超时(RTO)。Van Jacobson 在 1988 年给出的答案是指数加权移动平均(EWMA)。公式简洁到像是一首俳句:
srtt = (1 – α) × srtt + α × m
其中 m 是刚刚测得的 RTT 样本,α 是你选定的平滑因子。通常 α = 1/8。这意味着,旧估计值被保留 7/8 的权重,新样本只占 1/8。乍一看很保守。但你需要这种保守——否则一个突发的排队延迟就会让 RTO 剧烈震荡,导致整个连接陷入不必要的重传。想象你是厨房里的厨师,看着温度计调节炉火。你不能因为一秒的读数波动就大幅拧动燃气阀,那样汤必然会沸出来。EWMA 就是那个淡定的、只肯缓慢挪动阀门的老师傅。

不过,均值还不够。Jacobson 还引入了 RTT 方差(rttvar)的 EWMA,用绝对误差的平滑值来度量抖动:
rttvar = (1 – β) × rttvar + β × |srtt – m|
最终 RTO = srtt + 4 × rttvar。那个 4 是经验常数,建立在“RTT 分布近似高斯”的假设上。说实话,这个假设在如今的无线网络里时常失效,但它为二十年来的互联网提供了足够健壮的基础。直到 Google 决定把一切都赌在另一条思路上。
从纸面到线缆:一场 40% 吞吐量提升的压测实录
传统 TCP 的致命伤在于,它将丢包等同于拥塞。一旦检测到丢包,二话不说减窗。但在现代数据中心或长肥管道(LFN)上,丢包可能来自交换机缓存溢出前的瞬时负载,而非持续拥塞。BBR(Bottleneck Bandwidth and Round-trip propagation time)直接绕过这个逻辑,转而实时测量两个物理量:瓶颈带宽和最小 RTT。它就像开车时用导航软件看实时路况,而不是等到撞车(丢包)才减速。

我们有组公开压测数据能说明问题。在一条 100Mbps、延迟 30ms 的跨洲际连接上,故意引入 0.5% 的随机丢包。CUBIC(传统算法)的吞吐量直接被砸到不足 15Mbps。而 BBR v1 在同一路径下跑出 93Mbps,几乎打满带宽。重传率呢?CUBIC 飙到 12%,BBR 只有 0.3%——它几乎不制造不必要的重传。但先别急着赞美。BBR 的问题同样尖锐:它倾向于抢占公平带宽,跟其他流共存时像个莽撞的卡车司机,这就是 BBR v2 试图用 ECN 和丢包探测去解决的。
这种设计哲学上的决裂,让 RTT 的角色彻底改变。在传统 TCP 里,RTT 是用于计算 RTO 的输入;在 BBR 里,最小 RTT 直接决定发送速率的下界。因为发送速率 = 带宽 × 最小 RTT 窗口,只要窗口中灌满数据,管道就不会空洞。精准探测最小 RTT 于是变得性命攸关——然而多数落地案例都在这上面栽了跟头。
暗流涌动:RTT 落地时三个让你抓狂的坑

陷阱一:延迟 ACK 与 RTT 采样的冲突
TCP 的延迟 ACK 机制默认把确认包的发送延后 40ms 左右,期待攒够两个数据段再一起确认。这直接污染 RTT 采样:你测到的“RTT”实际上是 RTT + 延迟 ACK 等待时间。内核用 TCP timestamps 选项来解决——在每个数据段上打时间戳,接收方回显该值,这样即便 ACK 被延迟,RTT 依然能用时间戳精确计算。但很多旧设备或经过 NAT 的网络会丢掉 time stamps。我的建议:永远开启 net.ipv4.tcp_timestamps = 1,并在 NAT 侧检查 Option 头是否透传。如果实在开不了,考虑降低 delayed_ack 阈值(tcp_delack_min)或用应用层心跳包主动校准。
陷阱二:高丢包环境下的虚假重传与 RTT 痉挛
在无线链路或最后一跳 Wi-Fi 上,丢包是常态但非拥塞信号。内核的 RTT 估计器如果被重传包的 ACK 干扰(Karn 算法规定不更新 RTT),就会进入一种“信息饥饿”状态,RTO 可能无限膨胀或坍缩。RACK(Recent ACK)与 TLP(Tail Loss Probe)的组合能有效缓解:RACK 利用最近接收到的 ACK 时间推断丢包,而不是死盯 RTO;TLP 则快速重传尾部数据包,避免 RTO 触发。你需要确保内核版本 ≥ 4.9 并调整 net.ipv4.tcp_recovery 启用 RACK。
陷阱三:NAT 与负载均衡器中的 RTT 不对称
在存在双向路径不一致的网络上(比如三明治般夹着两个不同的 CDN 节点),往返时间可能并非对称。你测得的 RTT = 前向延迟 + 反向延迟,但内核以为路径是对称的,于是最小 RTT 估计产生偏差。我曾经在一个跨国视频会议系统里,因为反向链路走了劣质公网,BBR 的最小 RTT 被高估了 30ms,导致发送窗口膨胀、接收端丢帧严重。解决方案?不要只信内核的 RTT。在应用层实现单向延迟测量(比如 WebRTC 的 TWCC 或自定义 UDP 探测),将真实的基础传播时间喂给传输控制逻辑。或者用 eBPF 钩子劫持 RTT 样本,修正后注入内核。
工程之美从来不在于写出完美的代码,而在于当你明白所有底层机制后,能优雅地绕开它们的反噬。RTT 这个小东西,你越深入,越觉得它像一根紧绷在毫秒刻度上的弦——稍一拨弄,整个系统的音准就变了。