第 15 章:采集故障时,业务和证据分别怎样退化
可观测性系统帮助维护业务,也可能成为额外负担。若每次业务请求都同步等待远程日志写入,采集后端故障就会沿着新依赖拖垮业务。完全丢弃错误则又会让维护者在最需要证据时失明。正确设计需要明确两条链路的隔离与边界。
15.1 有限缓冲换来的不是无限承诺
本项目 SDK 使用批量处理器,业务线程主要负责记录,而网络导出在后台进行。这降低了直接耦合,但不能推导出“导出永不影响业务”或“所有信号都会补传”。缓冲、CPU、序列化、内存和后台线程仍是共享资源;压力过大时需要可解释的丢弃、退避或拒绝策略。
Collector 的 memory_limiter 和 batch 也有各自边界。多个位置的缓冲会增加故障窗口内可暂存数据的总量,同时增加恢复时的突发流量。只把队列数字不断调大,可能把短暂拥堵变成恢复后的长时间积压。
因此团队需要分别定义业务可用性、遥测接受率、导出延迟和数据缺失容忍度。审计类事件如果要求可靠保存,应采用与其交付合同匹配的系统,不应仅依赖一个普通 debug 日志管线。
15.2 一次受控的 Collector 停机实验
CI 在常规链路验收之后,停止自己创建的 Collector 容器,保持 Redis、RocketMQ、Tempo、Prometheus 和 Java 运行。此时提交新的订单,并检查 HTTP 仍返回 202、消费者确实写入处理标记。证据记录应用响应耗时,但不把一次通过当作所有压力下的隔离证明。
随后重新启动 Collector,等待健康入口,再提交 after-outage 请求,要求它的三个服务重新出现在同一个 Tempo trace 中。这里验证的是恢复后的新数据通路。对于停机窗口中的旧 trace,测试只记录恢复后的查询状态,不强制它必须存在,也不把一个 200 状态解释成所有 span 已完整补回。
这个区分很重要:后台 retry 可能救回部分数据,也可能在上限后放弃。一个旧 trace 只剩部分 span 时,后端仍然可能返回 200。若交付要求无损,需要专门检查完整性与持久化,而不能只检查 endpoint 可达。
15.3 恢复过程也需要观察
Collector 恢复后,应用积压的数据与新流量可能同时导出。应关注接收、拒绝、队列使用、导出失败和后台自身资源,而不是只看健康灯。生产中还需要避免重试把下游持续压在超载状态,明确队列满、磁盘满和后端限流时的行为。
本书的容器是专用 CI 资源,停机脚本不会操作用户已有服务。把同样方法用于共享环境时,必须先确认隔离、恢复入口和影响范围;不应为了演练擅自停止正在为他人服务的 Collector。
练习。 Collector 停机期间业务成功,恢复后旧 trace 查询返回 200。列出至少三项仍未被证明的事情。然后设计一个只针对专用实验环境的完整性检查,说明如何识别缺少的 span,而不是仅检查 trace 是否存在。
处理器与导出队列行为应核对所用版本的 Collector 配置与组件文档。