第 10 章:从一个 trace ID 还原因果图
彩色瀑布图很直观,但它是后端数据的一个呈现方式。为了让实验能在 CI 中独立核查,本书直接查询 Tempo 的 HTTP API,保留原始返回,再由检查器提取服务、操作名与父子关系。
10.1 从响应取得查询键
提交订单后,响应里的 trace_id 是本次执行的查询键:
curl -sS -H 'Accept: application/json' \
http://127.0.0.1:3200/api/traces/替换为本次trace_id32 位十六进制文本是人常用的表示形式。不同协议中的二进制 ID 可能在 JSON 里以 base64 表示,因此验收先标准化 ID,再比较父子关系。只比较两个看起来不同的字符串,而不确认编码方式,会得到虚假的“上下文断开”。
Tempo 可能需要一段时间才查询到刚结束的 span。验收使用有上限的轮询等待,不把首次 404 立即解释为永久丢失;同时也不会无限等待,让后端故障被隐藏。HTTP 200 也可能只包含先到达的一部分 span;验收会继续等到场景所需的服务与区间都出现,而不是遇到首个 200 就断言整条链路完整。采样为零的阶段则在确认业务已经发生、导出等待完成后,明确检查该 trace 不存在。
10.2 正常图形应满足哪些约束
normal 场景至少应能找到三个服务。网关 server 包围自己的 HTTP client,订单 server 从该 client 传播而来,Redis 与消息发布位于订单执行范围。消费者 parent 指向消息 producer,随后有自己的 Redis 处理结果区间。
这些约束比“span 数量等于七”稳定。如果将来增加数据库或连接池区间,span 数量变化并不一定是故障;但消费者错误挂到另一个请求上仍然是故障。本书验收同时保留 span 列表,使读者能够检查为什么某条断言成立,而不是只有一串布尔值。
服务名来自 Resource,不应该由 span 操作名猜测。一个 JVM 中可以有多个遥测资源,本实验用它区分三个逻辑服务;生产中还需要实例身份、部署环境和版本。版本是定位发布影响的重要维度,但如果把每次请求的随机数放进资源,后台就会把它们当成大量不同资源处理。
10.3 耗时可以比较,不能随意相加
客户端与服务端 span 往往重叠,异步消费也可能与入口尾部重叠。将所有 span 时长求和,可能大于用户实际等待时间。要分析关键路径,需要先明确哪一个结果是完成条件,再沿因果与依赖关系查看阻塞区间。
本实验的 redis-slow 与 worker-slow 分别改变同步和异步区间。前者会延长入口等待;后者可能在入口返回后继续增长。如果只按最长 span 排序,可能把一个并未阻塞用户响应的异步区间当成入口变慢原因。这也是为什么用户症状必须先被准确描述。
练习。 一条 trace 中网关总耗时 500 毫秒,订单 server 450 毫秒,消费者 800 毫秒。你能否由此断言用户等了 1750 毫秒?写出两种可能的时间关系,并说明需要哪些开始与结束时间才能判断。
查询路径与响应格式以 Tempo API 文档为入口,实验固定版本的实际返回保存在证据包中。