题干与适用场景
团队发现缓存网关会附加 Warning: 110 - "Response is stale" 和 Warning: 111 - "Revalidation failed"。浏览器和下游服务并不稳定地展示这些字段,新的标准也不再推荐使用它们。请说明 Warning 的历史语义、缓存验证边界、替代字段和无损迁移步骤。
这道题适合后端、网关、CDN 和平台岗位。重点是能区分协议元数据、用户错误和内部可观测性,不要只把弃用字段换成另一个自定义响应头。
面试官考察点
强回答会指出 Warning 可描述消息问题,缓存相关 1xx 警告在成功验证后可被缓存删除;但该字段已被弃用,因为生成和呈现都不广泛,部分信息可由 Age 等其他字段推断。候选人还应保留缓存契约,用 Cache-Control、Date、Age、状态码和日志/指标表达事实,避免客户端依赖自由文本。
回答前需要澄清的问题
- Warning 是源站、共享缓存还是边缘网关生成的?是否有多级代理?
- 110、111 只是诊断,还是客户端据此改变了业务行为?
- 当前响应是否包含
Date、Age、Cache-Control、ETag和Last-Modified? - 需要兼容哪些旧客户端,能否先双写一段时间?
- “新鲜”“过期”“重新验证失败”分别要给用户、业务和运维谁看?
30 秒回答框架
“我会先把 Warning 当成历史缓存诊断,而不是稳定的用户错误契约。110 表示缓存响应过期,111 表示重新验证失败,但字段已弃用且不适合承载自由文本。先盘点消费者和多级代理,用 Cache-Control、Date、Age、验证器和结构化指标表达真实状态;短期可在受控范围双写兼容字段,观察客户端行为后删除 Warning,并用缓存命中、过期服务、重新验证失败率和错误预算验证迁移。”
分步骤深入解答
第一步:还原 Warning 的历史职责
Warning 是请求和响应头,可包含三位警告码、生成代理和文本。缓存相关的 110、111 分别描述响应过期和重新验证失败;它们不是 HTTP 状态码,也不等价于 4xx 或 5xx。多个代理可能追加字段,因此自由文本的来源和可信度并不稳定。
第二步:说明为什么要迁移
MDN 将 Warning 标为弃用,原因是它没有被广泛生成或呈现,相关标准已移除这类通用警告的推荐用途。继续依赖它会把缓存诊断绑在不稳定的文本格式上,还可能让跨代理的重复、重排和过滤变得不可见。迁移应先证明没有客户端把它当作业务信号。
第三步:用标准缓存字段表达事实
Date 是响应生成时间,Age 表示共享缓存中的近似年龄;Cache-Control 定义新鲜度和重新验证要求,ETag 或 Last-Modified 供条件请求使用。它们共同描述缓存状态,不能用一个自定义 X-Warning 文本替代。源站仍应返回正确的 Vary,避免把不同请求的表示混在一起。
第四步:把诊断移到可观测性
网关在内部记录结构化事件,例如 cache_status=stale、revalidation=failed、upstream=timeout、命中层级和请求追踪 ID。日志要避免记录敏感 URL 查询参数;指标按路由、缓存键族和上游错误类型聚合。响应体只承担用户需要理解的状态,运维细节留在受控系统。
第五步:设计兼容双写窗口
先在影子流量或小比例路由上停止消费 Warning,确认没有 SDK、脚本或代理规则依赖它。若确有旧客户端,短期保留 Warning,同时发布替代字段或客户端升级指南;替代字段必须有稳定枚举和版本契约,不能复刻任意文本。为双写设置明确截止时间,避免兼容层永久存在。
第六步:处理多级缓存和验证失败
重新验证失败不一定意味着源站不可用,可能是网络超时、条件请求被拒绝或上游返回 5xx。网关要决定是提供旧的陈旧响应、返回错误,还是按 stale-if-error 等策略服务旧内容,并记录选择原因。不能因为删除 Warning 就掩盖过期服务或无限重试。
第七步:区分 103 Early Hints 等不同目的
103 Early Hints 是在最终响应前提示可能需要的资源,主要配合 Link 头进行预连接或预加载;它不是缓存过期警告,也不替代 Age 或 Cache-Control。面试中应明确不同字段的时序、消费者和失败语义,避免把所有“提示”都塞进 Warning。
第八步:分阶段下线并验证
迁移前后比较 Warning 读取量、缓存命中率、stale 服务比例、条件请求成功率、源站错误率、P95 延迟和客户端错误。先在单一缓存层关闭,再扩展到全链路;确认旧客户端无异常后移除生成逻辑、文档和告警规则。回滚开关应只恢复兼容输出,不恢复对 Warning 的业务依赖。
设计取舍与边界
保留 Warning 的短期兼容成本低,但会延长不稳定契约和排障歧义。立即删除可减少协议噪声,却可能打断遗留脚本。最安全的迁移是先观察真实依赖,再用标准缓存字段和结构化可观测性承接需求。
不要把 Age 解释成源站生成时间,也不要把 Cache-Control: no-cache 误解为禁止缓存;它要求复用前重新验证。缓存策略、验证器和错误响应必须结合业务可接受的新鲜度共同设计。
落地计划与证据
第一周导出边缘、共享缓存和客户端对 Warning 的读取路径,建立按路由和缓存层的基线。第二步在一个无关键业务依赖的路由双写结构化指标,比较 Age、条件请求和错误率。第三步按缓存层逐步关闭 Warning,并保留一键恢复兼容输出的配置。
验收条件包括:没有生产客户端读取 Warning;缓存命中和重新验证成功率不下降;stale-if-error 次数有解释;日志与指标可按追踪 ID 关联;文档、SDK 和告警规则已同步更新。
常见误区与追问
把 110 当成 4xx 或 5xx
110 是历史 Warning 代码,不是响应状态码。业务错误应由状态码和响应契约表达,缓存诊断由指标表达。
用自定义 X-Warning 复制旧问题
自由文本、代理追加和版本兼容仍会造成同样的不确定性。若必须兼容,应使用稳定枚举、版本和明确消费者。
只删除响应头不补可观测性
删除字段不会修复过期服务或重新验证失败。先建立缓存状态指标、追踪和告警,再移除外部头。
把 103 Early Hints 当作缓存警告
103 的时序和用途是提前提示资源,不能表达缓存新鲜度或验证失败。应分别设计字段和处理路径。
如何证明可以下线?
观察客户端读取量、代理配置、错误率和关键缓存指标,运行双写窗口并在单层灰度后扩展。没有依赖证据时不要直接全网删除。