4、现代RTO计算方法:RTT的采样与平滑、RTT的偏差计算、RTO的计算公式、最小RTO与最大RTO的限制
聊完超时重传的基本概念,咱们得进入一个更核心的环节——RTO(Retransmission Timeout)到底怎么算?
说白了,RTO 就是发送方在发出数据包后,愿意等多久才认为它丢了。这个值设得太短,容易造成大量不必要的重传,浪费带宽;设得太长,应用层感知到的延迟就会飙升。我早年调优过一个跨国传输的服务,就是因为 RTO 默认值太保守,导致用户频繁抱怨卡顿。嗯,从那以后我对 RTO 的计算公式就格外上心。
现代 TCP 实现(比如 Linux 内核中的实现)已经不依赖静态配置了,而是通过动态采样 RTT,再经过平滑、偏差计算,最终得出一个自适应的 RTO。下面我们一步步拆开看。
4.1 RTT的采样与平滑
RTO 的基础是 RTT(Round-Trip Time),也就是数据包从发出去到收到确认所经历的时间。但网络环境是波动的,单次 RTT 样本不能直接拿来用——万一这次刚好碰上网络抖动呢?
所以 TCP 引入了指数加权移动平均(EWMA),对 RTT 做平滑处理。公式如下:
SRTT = (1 - α) × SRTT_old + α × RTT_sample
其中 α 是平滑因子,标准推荐值是 1/8(0.125)。这个值意味着新样本的权重不大,历史趋势占主导,能有效过滤掉瞬时噪声。
4.2 RTT的偏差计算
光有平滑后的 SRTT 还不够。你想想看,如果网络延迟突然翻倍,SRTT 反应慢半拍,RTO 还是按老值算,那重传就会滞后。所以我们需要一个偏差量来兜底。
这个偏差叫 RTTVAR(RTT Variation),同样用 EWMA 方式计算:
RTTVAR = (1 - β) × RTTVAR_old + β × |SRTT - RTT_sample|
β 的推荐值是 1/4(0.25)。注意这里用的是绝对偏差,而不是方差——因为计算方差需要平方运算,在嵌入式或早期网络栈中开销太大。用绝对偏差是工程上的折中,效果已经足够好。
4.3 RTO的计算公式
有了 SRTT 和 RTTVAR,RTO 的计算就水到渠成了。RFC 6298 给出的标准公式是:
RTO = SRTT + max(G, 4 × RTTVAR)
这里 G 是时钟粒度(clock granularity),通常是 1ms 或 10ms。为什么要取 max?因为当 RTTVAR 很小时(比如局域网环境),4×RTTVAR 可能比时钟粒度还小,这时候用 G 兜底,避免 RTO 被截断得太小。
我举个例子你就明白了:
| 场景 | SRTT (ms) | RTTVAR (ms) | 4×RTTVAR (ms) | G (ms) | RTO (ms) |
|---|---|---|---|---|---|
| 局域网 | 1 | 0.1 | 0.4 | 1 | 2 |
| 跨洋链路 | 200 | 30 | 120 | 1 | 320 |
| 无线网络(抖动大) | 100 | 50 | 200 | 1 | 300 |
你看,局域网场景下 4×RTTVAR 只有 0.4ms,比时钟粒度还小,所以 RTO 实际上是 SRTT + G = 2ms。这个设计很巧妙,避免了过于激进的超时。
4.4 最小RTO与最大RTO的限制
公式算出来的 RTO 还不能直接用——必须加上上下限。为什么?
- 最小 RTO:防止 RTO 过小导致虚假重传。RFC 6298 建议下限为 1 秒。但现代操作系统普遍觉得 1 秒太保守了,Linux 默认是 200ms(可通过
tcp_rto_min调整)。 - 最大 RTO:防止重传间隔无限增长。标准上限是 60 秒。超过这个值,TCP 会认为连接已经彻底断了,直接放弃。
最终 RTO 的取值逻辑可以总结为:
RTO_final = clamp(RTO_calculated, RTO_min, RTO_max)
其中 clamp 函数将值限制在 [RTO_min, RTO_max] 区间内。
4.5 整体计算流程
下面这张图把整个 RTO 计算链路串起来了,你可以对照着看:
整个流程走下来,你会发现 RTO 不是拍脑袋定的,而是基于实时网络状态动态调整的。SRTT 反映基线延迟,RTTVAR 反映抖动幅度,上下限则防止极端情况。这三者配合,才构成了现代 TCP 栈中可靠的超时重传基础。