溯源链路基本概念:Trace、Span、Context,调用链模型
做溯源链路,你得先弄明白几个核心概念。说白了,它们就像盖楼的砖、水泥和图纸。我最早接触这个东西,是在排查一个线上慢接口时,日志散落得到处都是,根本串不起来。后来才发现——原来缺的就是一套标准化的链路模型。
Trace —— 一次请求的完整旅途
Trace 代表一次完整的请求处理过程。从客户端发起,到后端多个服务接力,最后返回响应——整条路径就是一个 Trace。
每个 Trace 有一个全局唯一的 Trace ID,你可以把它当作快递单号。有了它,所有参与这次请求的服务都能通过这个 ID 关联起来。
- Trace ID 全局唯一,通常由网关或入口服务生成
- 覆盖请求经过的所有微服务、中间件、数据库
- 一条 Trace 由多个 Span 组成
Span —— 链路中的最小工作单元
Span 是 Trace 里的一个工作片段。一次 RPC 调用、一次数据库查询、一段业务逻辑——都可以用一个 Span 来表示。
每个 Span 也有自己的 ID,并且会记录它的父 Span ID。这样一来,Span 之间就形成了树状层级关系。我遇到过不少新手,以为只要记个 Trace ID 就够了,结果定位慢请求时还得人工猜调用顺序——这就是忽略了 Span 父子关系的后果。
| 属性 | 说明 | 我常用的字段名 |
|---|---|---|
| spanId | 当前 Span 唯一 ID | uuid 短格式 |
| parentSpanId | 父 Span ID,根 Span 为空 | 直接关联上一级 |
| operationName | 操作名称,如 POST /api/order |
动词 + 资源路径 |
| startTime / duration | 开始时间与耗时 | 毫秒时间戳 |
| tags | 关键标签,如 responseCode、error | 按需打标 |
Context —— 链路上的“快递员”
Context 负责把 Trace ID 和 Span ID 在服务之间传递。没有 Context,就算你记录了日志,也连接不起来。
常见的传递方式:HTTP 请求头、gRPC metadata、消息队列的 header。我自己在项目里,一般会在入口拦截器里自动注入 x-trace-id 和 x-span-id,保证后续所有子调用都能继承上下文。
调用链模型 —— 它们怎么组合在一起?
把这几个概念合起来,就是调用链模型。我一般这样理解:
- 用户请求到达网关,生成 Trace ID,创建根 Span。
- 网关调用服务A,通过 Context 传递 Trace ID + 当前 Span ID 给服务A。
- 服务A收到后,创建子 Span,把父 Span ID 设为网关传过来的 Span ID。
- 服务A调用服务B,同样传递 Context…… 如此递归。
- 所有 Span 上报到链路系统(如 Jaeger / Zipkin),按 Trace ID 聚合,按父子关系排列,就形成了完整的调用链。
一张图看懂调用链模型
下面是 Trace、Span、Context 的关系图。你可以看到:一个 Trace 里嵌套多个 Span,Context 沿着箭头在服务间流转。
实际生产中的例子
假设有一个下单接口。用户点击下单后,链路大致是:
- API 网关收到请求,生成 Trace ID =
t-001,创建根 Span。 - 网关调用订单服务,HTTP header 里带上
traceparent: 00-t-001-root-span。 - 订单服务收到后,创建子 Span,执行库存扣减(再调用库存服务),再次传递 Context。
- 库存服务执行完,把结果、耗时、状态码打标到自己的 Span,并上报。
- 最终所有 Span 按 Trace ID 聚合,展示出完整的调用链:网关→订单→库存。
清楚了 Trace、Span、Context,你就掌握了溯源链路的骨架。剩下的就是怎么埋点、怎么传输、怎么存储。心里有了这个模型,后面编码才不会跑偏。