题干与适用场景
你负责支付、改密或创建订单 API。入口启用了 TLS 1.3 0-RTT,网关可能把带有 Early-Data: 1 的请求转发给服务。请说明何时返回 425、何时继续处理,以及客户端如何在握手完成后重试。题目适合后端、平台和 API 基础设施岗位。
RFC 8470 定义 425 是服务器不愿冒险处理可能被重放的请求;它不是“服务器太忙”的通用重试码。下面假设网关能保留 Early-Data 信号,服务能区分只读请求和有副作用请求。
面试官考察点
- 能否把 0-RTT 的性能收益与重放风险分开,而不是把所有 4xx 都当作客户端输入错误。
- 能否沿着客户端、网关、应用三段数据流说明谁负责等待、重试和幂等。
- 能否指出 RFC 对 425 的发出条件、不可缓存性和重试时机,并把它转成可测试的 API 策略。
普通回答只背出“425 是 Too Early”。强回答会按副作用、Early-Data 标记、幂等键和重试窗口做决策,并说明错误配置会如何导致重复扣款。
回答前需要澄清的问题
- 请求是否带
Early-Data: 1,或入口是否能证明它来自早期数据?RFC 建议服务没有这些信号时不要随意返回 425。 - 操作是否有外部副作用?读取订单可以继续;扣款、改密和发券需要等待握手,或先建立严格的幂等状态。
- 网关是否会自动重试?如果会,必须确认它在握手完成后重试,并且不会让应用和网关各自重试一次。
- 客户端是否支持幂等键和重试上限?没有这些能力时,宁可关闭该类请求的 0-RTT,也不要把 425 当作无限重试许可。
30 秒回答框架
“我先确认请求是否来自带 Early-Data: 1 的 TLS 早期数据,再判断它是否有副作用。只读请求可以继续;支付、改密等写请求先返回 425 或在网关等待握手。客户端只在握手完成后重试,服务端用幂等键和唯一约束防止重复执行。网关要保留信号、避免双重重试,并监控 425 比例、重试成功率和重复副作用。若链路无法证明这些条件,我会关闭该端点的 0-RTT。”
分步骤深入解答
1. 先识别风险信号
RFC 8470 要求中间层在收到早期数据时保留 Early-Data 语义;服务端只有在请求可能被重放时才应考虑 425。没有信号却返回 425,会把普通网络失败误报成安全拒绝。
2. 按副作用分类
把端点分成只读、可安全重复和不可安全重复三类。GET /orders/123 通常属于只读;生成一次性优惠券、扣款和修改密码属于不可安全重复。后者应等待完整握手,或由应用在持久化层先记录幂等键。
3. 规定单一重试责任
客户端收到 425 后,必须等待 TLS 握手完成,再发送同一请求;重试不能再次走早期数据。网关可以代重试,但要把责任写入契约,避免网关和 SDK 同时重试。设置指数退避、次数上限和可观测的重试原因。
4. 把幂等作为第二道防线
对写请求要求客户端生成幂等键。服务端用租户、端点和键建立唯一记录,状态至少区分处理中、成功和可重试失败;相同键重复到达时返回原结果或明确的处理中状态。幂等键不能替代 425,因为首次处理仍可能在数据库提交后于响应前被重放。
5. 处理网关与多实例一致性
所有实例都要使用同一 Early-Data 策略。网关若不确定上游是否理解该信号,应等待握手或直接拒绝;不能把早期写请求静默转给只识别普通 HTTP 的服务。日志记录请求 ID、Early-Data、425 原因和幂等键哈希,避免记录支付内容。
6. 验证失败路径
测试带与不带 Early-Data: 1 的请求、握手完成后的首次重试、网关自动重试、客户端超时后再次提交,以及两个实例并发处理同一幂等键。断言不可重复副作用只发生一次,且 425 不被缓存。
替代方案是完全关闭 0-RTT:安全边界简单,代价是增加首个请求的握手延迟。若端点数量少、延迟收益不重要,关闭往往比维护跨层策略更稳妥。
高质量示范回答
“我不会把 425 当成普通限流错误。先看请求是否带 Early-Data: 1,再看操作是否有副作用。订单查询可以继续;支付和改密在网关等待握手,或由服务返回 425。客户端收到后只能在握手完成后重试一次,SDK 的上限和退避要写入契约。服务端还要求幂等键,数据库用租户加键做唯一约束,并保存处理中和最终结果,防止响应丢失导致重复扣款。所有网关和实例采用同一规则,监控 425、重试成功率和重复执行告警。如果链路无法传递 Early-Data,我会关闭写端点的 0-RTT,而不是猜测请求是否安全。”
常见错误
- 把 425 当作 429 的替代品 → 触发原因和客户端动作不同 → 只在早期数据重放风险成立时使用 425。
- 所有请求都返回 425 → 读请求被无谓阻塞,客户端可能无限重试 → 按副作用分类并设置上限。
- 重试仍走 0-RTT → 重放窗口没有关闭 → 明确要求握手完成后再发。
- 只在应用内做幂等 → 网关或另一个实例可能先执行副作用 → 在入口、应用和持久化层统一策略。
- 记录完整支付体排查重放 → 日志暴露敏感数据 → 只记录请求 ID、原因和脱敏后的幂等键。
追问及应对
如果网关移除了 Early-Data 头,你如何处理?
我会把它视为能力缺失:网关要么等待握手后再转发,要么拒绝早期请求。应用不能从普通请求中推断它曾经使用 0-RTT;先修复信号传递,再启用写端点。
如果客户端在收到 425 前已经超时并再次提交呢?
用同一个幂等键接住两次提交,第二次读取第一笔的处理中或最终状态。对没有幂等键的危险写请求,返回可诊断错误并进入人工或补偿流程,不自动猜测是否已扣款。
如果重试成功率很高但延迟 SLO 变差,你会怎么取舍?
按端点和用户代理分段比较 425 率、p95 首次成功延迟、重复副作用和业务成功率。若写请求收益不足以抵消延迟,我会只保留安全读请求的 0-RTT,或直接关闭该类端点。