第 14 章:用四个故障场景练习排查顺序
工具齐全以后,排查仍然可能沿着错误方向前进。本章不新增后台,而是用相同系统、相同接口的四种输入,练习如何提出假设、收集反证并缩小结论范围。
14.1 同步等待与异步等待
先运行 normal,保存入口 trace ID、消费者事件和完整 span 列表。再运行 redis-slow,唯一关键变化是客户端区间内等待 350 毫秒。比较结果时,先看入口总区间,再查看订单 server、Redis client 和发布区间。若长区间落在 Redis client 范围,只能把排查范围缩小到该调用周围;你仍需知道注入实际发生在命令前,才能准确说出原因。
随后运行 worker-slow,将消费者等待设为 450 毫秒。此时需要先确认入口的完成条件:它返回已接受,不等待消费结果。消费者变慢主要影响异步完成时间,不一定让入口同幅度变慢。若告警只盯入口延迟,这个业务风险可能被遗漏。
两种输入使用不同的故障注入位置,这比在所有请求结束后统一 sleep 更有教学意义。后者会让曲线变慢,却无法检验我们是否识别了具体依赖和完成边界。
14.2 业务正常与追踪断点
broken-context 的业务状态与正常请求相似,但不注入消息属性。先看订单是否处理,再看 worker 日志的 trace ID,最后查询两个独立 trace。若先在入口 trace 里寻找不存在的 worker span,容易把问题误判成“消息没有消费”。
这类场景提醒我们,业务观测与遥测管线要互相核对。业务标记证明某次处理确实发生,独立 trace 证明消费者也被仪表化;两者不共享上下文,说明断点位于传播而非业务投递。
14.3 应用错误与告警证据
受控失败返回 503,不发送新消息。对该场景,应该看到错误计数增长与对应 ERROR span,而不是等待一个不可能出现的消费事件。告警根据群体错误率触发,trace 则解释某一次失败。查询顺序可以是“告警确定服务和窗口 → 错误指标定位范围 → 事件日志取得具体 trace → trace 检查失败位置”。
不要把某次错误 trace 的存在当作所有错误都被保留。采样比例、导出失败和保留策略都会改变可查询集合,因此统计分母仍应来自合适的指标,而不是 trace 搜索命中数量。
14.4 基础设施就绪检查本身也会出错
本项目第一轮真实 CI 运行曾出现一个很具体的问题:mqadmin updateTopic 打印连接异常,但退出码为零。原脚本把退出码当作主题可用,过早启动应用,首个发布得到 MQClientException。容器 running 与 CLI 返回零,两项表面检查同时通过,业务仍失败。
修正后的脚本检查主题创建成功文本,并查询 NameServer 实际路由,确认出现 brokerAddrs 后再继续。这一经历不是虚构生产事故,而是本书实验开发中的可复核失败窗口。它说明 readiness 应当验证后续工作依赖的语义条件,不能只验证“命令被调用过”。对应失败和修正运行链接记录在验证附录。
练习。 为“入口变慢、消息完成也变慢”写一份五步排查顺序,至少包含一项可能推翻 Redis 假设的证据。再把主题就绪检查推广到数据库或搜索后台:说明什么样的 API 结果才代表你的业务下一步可以安全开始。