3. QUIC 超时机制基础:超时分类与计算模型
聊 QUIC 超时诊断,得先搞清楚它有哪些超时。TCP 那套超时模型你肯定熟——RTO、重传定时器,说白了就那几样。但 QUIC 不一样,它把超时拆成了三个独立维度:连接超时、空闲超时、握手超时。我刚开始调 QUIC 时也犯过糊涂,以为跟 TCP 一样调个 RTO 就完事了,结果线上丢包率一高,连接全断了。
嗯,这一节咱们就把这三个超时的定义、参数配置和计算模型讲透。你理解了这个基础,后面诊断超时问题才能找到根因。
3.1 三类超时的本质区别
先看一张图,把三类超时的触发条件和作用域理清楚。
这张图很关键。你注意看:连接超时覆盖整个握手阶段,但它的触发条件是「没有任何数据交换」;握手超时虽然也在握手阶段,但它只关心「握手消息是否到达」。说白了,连接超时是更粗粒度的兜底机制,握手超时是细粒度的重试机制。
3.2 超时参数配置
QUIC 的超时参数不像 TCP 那样藏在系统内核里,它暴露在应用层。我用过的几个主流 QUIC 实现,配置方式大同小异。下面这张表是我整理的常用参数:
| 参数名 | 所属超时类型 | 默认值 | 推荐范围 | 说明 |
|---|---|---|---|---|
initial_rtt |
连接超时 | 500ms | 100ms - 1s | 初始 RTT 估计值,影响首次超时计算 |
max_idle_timeout |
空闲超时 | 30s | 10s - 300s | 无数据时保持连接的最大时长 |
handshake_timeout |
握手超时 | 10s | 5s - 20s | 握手完成的最长等待时间 |
max_handshake_retries |
握手超时 | 5 次 | 3 - 10 次 | 握手消息最大重试次数 |
probe_timeout |
空闲超时 | 15s | 5s - 60s | 空闲探测间隔,小于 max_idle_timeout |
max_idle_timeout 调到 60s,避免用户切后台后频繁重建连接。但服务端之间通信,30s 足够了,太长反而浪费资源。
3.3 超时计算模型
QUIC 的超时计算不是拍脑袋定的,它有一套基于 RTT 的动态算法。我拆成三步来讲:
3.3.1 基础 RTT 采样
QUIC 每个包都带时间戳,所以 RTT 采样比 TCP 更精准。核心公式就一个:
// 伪代码:RTT 采样
sample_rtt = 当前时间 - 包发送时间 - 对端处理延迟
// 平滑 RTT 更新(指数加权移动平均)
srtt = (1 - α) × srtt + α × sample_rtt
// α 通常取 0.125
这里有个坑:对端处理延迟必须减掉。我遇到过一个问题,服务端处理请求花了 200ms,客户端算出来的 RTT 直接翻倍,导致超时时间虚高。后来加了处理延迟补偿,才恢复正常。
3.3.2 超时时间计算
QUIC 的超时时间不是固定值,它基于 srtt 和 rttvar(RTT 方差)动态调整:
// 超时时间计算
rttvar = (1 - β) × rttvar + β × |srtt - sample_rtt|
// β 通常取 0.25
// 超时时间 = srtt + 4 × rttvar
timeout = srtt + 4 × rttvar
// 但有个下限,防止超时过短
timeout = max(timeout, 1000ms) // 至少 1 秒
为什么加 4 倍方差?说白了就是留余量。网络抖动大的时候,rttvar 会变大,超时时间自动延长。我见过有人把系数改成 2,结果网络一波动就疯狂重传,把链路打爆了。
3.3.3 三类超时的具体计算
三类超时在基础公式上各有调整:
- 连接超时:直接用
initial_rtt + 4 × initial_rttvar,其中initial_rttvar默认是initial_rtt / 2。因为没有历史数据,所以第一次超时比较保守。 - 空闲超时:固定值
max_idle_timeout,不参与动态计算。但内部有个probe_timeout,用于发送 PING 帧探测对端是否存活。 - 握手超时:每次重试后超时时间翻倍(指数退避),但不超过
handshake_timeout上限。
关键区别: 连接超时和握手超时都涉及指数退避,但连接超时退避到上限后直接断开,握手超时退避到上限后还可以继续重试(直到达到 max_handshake_retries)。
3.4 避坑指南
讲几个我踩过的坑:
我曾经把 max_idle_timeout 设成 5s,想着快速释放空闲连接。结果用户手机切到后台 3 秒,回来连接就断了,体验极差。后来改成 30s 才稳定。
还有一个常见问题:连接超时和握手超时同时触发怎么办? QUIC 的规则是:谁先到谁先触发。如果连接超时先到,直接断开;如果握手超时先到,重试握手消息。我建议你把连接超时设得比握手超时略长,比如握手超时 10s,连接超时 12s,这样握手有足够的重试机会。
调试技巧: 用 quic-trace 抓一把日志,搜索 timeout 关键字。你会看到类似 handshake_timeout fired after 10023ms 的日志,对照时间戳就能定位是哪个超时触发了。
3.5 小结
这一节我们把 QUIC 的三类超时讲透了。你记住三个数字:连接超时 5-10s、空闲超时 30s、握手超时 10s。但别死记硬背,要根据你的网络场景调。移动端、数据中心、跨国链路,参数都不一样。
嗯,超时机制是 QUIC 可靠性的基石。你理解了这些,后面诊断超时问题时,才能一眼看出是参数配错了,还是网络真的断了。