题目与背景
一个 HTTP/3 网关承载长连接和多路并发请求。发布新版本时,希望拒绝新请求、让已接受请求完成,并在超时时安全关闭连接。请基于 RFC 9114 设计 GOAWAY 下线流程,并说明它和 QUIC 连接关闭、HTTP 请求重试的边界。
面试官考察什么
重点是区分 GOAWAY 的“限制后续请求”与 CONNECTION_CLOSE 的“终止 QUIC 连接”。HTTP/3 GOAWAY 携带的是请求流 ID 上界,服务端可先发送较大的值再递减到最终边界。答案还应覆盖双向并发、客户端发现未处理请求、幂等性、代理传播、超时和观测。
先问清楚的澄清问题
请求和连接形态
确认是否有长轮询、上传、WebTransport 或非幂等 POST,以及连接是否经过反向代理。不同流的完成时间和重试风险不同。
下线目标和超时
明确目标是连接排空、节点维护还是故障隔离,设置最大排空时间、硬关闭时间和发布批次。没有硬上限会让旧连接无限占用资源。
客户端能力
确认客户端和代理是否正确处理 GOAWAY、是否支持连接迁移和重连,以及是否能区分“服务器拒绝新流”和“请求已执行但响应丢失”。
30 秒回答框架
“HTTP/3 GOAWAY 让端点声明一个请求流 ID 上界,收到后不能再创建超过边界的新请求。服务端下线先停止接收新连接,再发送较宽的 GOAWAY 观察并发请求,随后递减到最终边界,让客户端知道哪些请求可能未处理。排空期间保留已接受流,超时后用 QUIC CONNECTION_CLOSE 结束。客户端只在确认请求未执行且方法可安全重试时重试。”
深入解答步骤
第一步:区分 GOAWAY 与 CONNECTION_CLOSE
GOAWAY 是 HTTP/3 控制消息,限制请求流的最大允许 ID;它不会立刻终止连接。CONNECTION_CLOSE 则关闭 QUIC 连接,可能中断仍在运行的请求。下线逻辑先用 GOAWAY 排空,再在硬超时后关闭。
第二步:采用先宽后窄的边界
服务端可先发送一个较大的 ID,覆盖已经观察到的并发请求;随后在停止接收新工作后发送更小的最终 ID。客户端据此判断哪些流可能被接受,不能把递减过程当作允许新请求的信号。实现要保证控制帧顺序和状态持久化。
第三步:处理已接受、未开始和未知请求
低于最终边界且已创建的流继续完成;高于边界的请求视为未被接受,客户端可在新连接重试。若服务端无法判断某个非幂等请求是否执行,必须返回明确的应用幂等键或人工核对路径,而不是盲目重放。
第四步:设计代理和连接迁移策略
反向代理收到上游 GOAWAY 后,应停止向该连接分配新请求,并把状态传播给下游。QUIC 连接迁移不等于请求迁移;下线期间不要假定换了网络路径就能安全转移正在执行的流。
第五步:设置排空与硬关闭
记录 GOAWAY 发送时间、最后活动流和剩余请求。达到排空超时后取消仍未完成的流,再在硬关闭时间用 CONNECTION_CLOSE 释放连接。发布控制器要限制同时下线的实例数,避免容量骤降。
第六步:保护重试与副作用
只对确认未执行的幂等方法或带幂等键的写操作重试,使用指数退避和预算。响应丢失不代表请求未执行;支付、扣库存等副作用必须通过服务端幂等记录去重。
第七步:验证发布效果
在灰度中注入长请求、并发流、客户端重连、GOAWAY 递减、代理转发和硬关闭。监控新流拒绝数、排空耗时、未完成流、重试率、重复副作用、连接错误和容量余量,并验证滚动发布期间 p99 延迟与错误率。
高质量示例回答
我会先停止新连接分配,再发送宽范围 GOAWAY 让客户端停止创建新流;确认并发请求后发送更小的最终边界。低于边界的已接受流继续完成,超时仍未完成的流才取消并以 CONNECTION_CLOSE 释放连接。客户端只能对确认未执行且幂等的请求重试,非幂等写操作必须依赖幂等键。
代理要传播下线状态,连接迁移不能当作请求迁移。灰度验证长请求、并发、递减 GOAWAY、重连、超时和副作用去重,监控排空、拒绝、重试、重复执行和容量指标。
常见错误
- 错误: 一发送 GOAWAY 就关闭 QUIC。→ 原因: 仍在运行的请求会被中断。→ 改进: 先排空,硬超时后再 CONNECTION_CLOSE。
- 错误: 把 GOAWAY 上界当作已执行请求清单。→ 原因: 流 ID 只表示允许范围,不证明应用完成。→ 改进: 用服务端状态和幂等键确认执行结果。
- 错误: 所有失败请求都自动重试。→ 原因: 响应丢失可能伴随副作用。→ 改进: 限定幂等方法或带幂等键的写操作。
- 错误: 连接迁移能转移正在执行的请求。→ 原因: QUIC 路径变化不改变 HTTP 流状态。→ 改进: 通过新连接重新发起安全可重试请求。
追问与回答
追问 1:为什么 GOAWAY 可以递减?
服务端需要先覆盖已经观察到的并发请求,再确定最终不再接受的新流边界。递减能让客户端逐步收敛到安全边界,而不必一开始猜测所有并发流。
追问 2:客户端如何判断请求是否被接受?
比较请求流 ID 与最终 GOAWAY 边界只能判断可能范围,不能证明应用是否执行。客户端还要结合响应、连接错误和应用幂等查询。
追问 3:GOAWAY 会跨代理自动传播吗?
不会假定自动传播。代理作为独立 HTTP/3 端点,需要把上游排空状态转换为下游连接和路由策略,并避免在已下线连接继续分配新流。
追问 4:硬关闭前为什么还要取消流?
先取消可以释放应用资源并记录明确原因,让客户端区分排空超时与网络故障。随后关闭 QUIC 连接是最终资源边界,不能代替应用层清理。