灰度只覆盖了一小部分入口,而全量发布改变了入口组合、缓存层级和回源路径,404notfound 的触发条件因此从“个别样本成立”变成“规模化后失效”。要判断问题出在哪,先分清是样本选择偏差,还是发布动作本身引入了新变量。
假设一次发布把商品详情页从旧路径迁到新路径,灰度只放量了1%的入口,且这些入口恰好都来自站内推荐位。灰度期间新旧路径并行,旧路径仍可访问,404notfound 只在极少数手工构造的链接上出现,看起来可控。全量切换后,站外收藏、历史分享链接和部分联盟渠道的落地页开始批量报404,监控曲线才突然抬升。
这个矛盾不是灰度“测少了”,而是灰度没有覆盖真正会触发例外的入口类型。灰度样本成立,只能说明被选中的那部分成立;它不能证明未被选中的入口也成立。
第一种解释是样本偏差。灰度流量集中在站内来源,这些来源在发布时同步改写了链接;站外来源没有参与灰度,旧链接在全量后才被真正切断。此时404notfound 是入口覆盖不足的产物,与代码逻辑无关。
第二种解释是发布动作改变了运行规则。全量发布可能同时刷新了CDN缓存、调整了反向代理的重写规则,或让应用从单实例变成多实例。灰度时命中旧缓存的请求仍能返回内容,全量后缓存被清空,未同步更新的重写规则才暴露出来。这类情况下,404notfound 不是入口问题,而是环境差异问题。
把404日志按入口来源、User-Agent、请求路径前缀和回源节点分组,观察分布形状:
可执行的动作是:在灰度阶段就按入口来源分层抽样,而不是只按流量比例抽样。若灰度只覆盖站内入口,就应把站外来源单独列为一层,哪怕每层只取很小流量。这个动作的结果会直接决定下一步——分层后仍无404,说明入口覆盖足够,问题更可能在环境;分层后立刻复现404,说明发布前就需要准备旧路径的过渡处理。
灰度流量小,不等于风险小;灰度流量大,也不等于覆盖全。判断灰度结论能否外推,至少要看三点:入口来源是否覆盖了主要落地页类型、新旧路径是否在灰度期间真正并行、缓存与重写规则是否与全量环境一致。任何一点不满足,灰度里的“正常”都不能直接搬到全量。
另外,404notfound 数量归零也不能单独证明处理正确。它可能是监控未覆盖该入口,可能是缓存仍在提供旧内容,也可能是请求被重定向到其他状态码。要结合访问日志、回源日志和入口清单交叉确认,而不是只看一个计数。
假设某站点灰度只对登录用户开放新路径,未登录用户仍走旧路径。灰度期间404notfound 为零,因为登录用户的会话里保存了新路径映射。全量后未登录用户直接访问旧链接,404开始出现。这里的证据是:按登录状态分组后,404全部落在未登录分组。这个例子说明,灰度分层如果漏掉了“未登录”这一层,结论就不能外推。修复动作是给旧路径加过渡重定向,再观察未登录分组的404是否下降;若下降,说明入口覆盖是主因;若不降,再查重写规则和缓存。
灰度发布的目标不是让指标好看,而是让例外在可控范围内出现。可操作的做法是:发布前列出所有入口来源和路径类型,按来源分层放量;发布中同时观察新旧路径的状态码分布,而不是只看总量;发布后保留旧路径一段时间,确认站外来源的404趋势回落再下线。这样,全量发布时遇到的例外,大多已经在灰度阶段被记录过,而不是第一次出现。