2. Jaeger 数据模型:Trace、Span、SpanContext 的关系与 Span 结构
在 Jaeger 的分布式追踪体系中,Trace、Span 和 SpanContext 是三个最核心的数据抽象。理解它们之间的层级关系与 Jaeger 特有的 Span 结构,是后续进行标签复盘与性能分析的基础。
2.1 核心概念定义
| 概念 | 定义 | 生命周期 |
|---|---|---|
| Trace | 一次完整请求或事务在分布式系统中的全路径追踪,由一组具有相同 TraceID 的 Span 组成。 | 从入口请求开始到最终响应结束。 |
| Span | Trace 中的单个工作单元,记录一个操作(如 RPC 调用、数据库查询)的开始、结束、标签和日志。 | 从操作开始到操作结束。 |
| SpanContext | Span 的上下文标识,包含 TraceID、SpanID、Baggage 等,用于跨进程传递追踪状态。 | 随 Span 创建而生成,随 Span 结束而归档。 |
关键原则: Trace 是 Span 的有向无环图(DAG),SpanContext 是连接这些 Span 的“信使”。没有 SpanContext,跨服务的 Span 无法关联到同一个 Trace。
2.2 三者的层级关系
下图展示了 Trace、Span、SpanContext 之间的包含与引用关系。一个 Trace 包含多个 Span,每个 Span 内部携带一个 SpanContext,SpanContext 通过 TraceID 和 ParentSpanID 形成父子链路。
复盘提示: 在 Jaeger UI 中查看 Trace 时,注意观察每个 Span 的
ParentSpanID。如果某个 Span 的 ParentSpanID 指向了一个不存在的 SpanID,说明该 Trace 存在“断链”,通常由采样不一致或上下文丢失导致。
2.3 Jaeger 特有的 Span 结构
Jaeger 的 Span 结构在 OpenTracing 标准基础上进行了扩展,增加了 Jaeger 特有的字段。以下是 Jaeger Span 的完整数据结构(以 JSON 序列化为例):
{
"traceID": "abc123def456",
"spanID": "a1b2c3d4",
"operationName": "HTTP GET /api/users",
"references": [
{
"refType": "CHILD_OF",
"traceID": "abc123def456",
"spanID": "root_span_id"
}
],
"startTime": 1680000000000000, // 微秒
"duration": 150000, // 微秒
"tags": [
{"key": "http.method", "vType": "STRING", "vStr": "GET"},
{"key": "http.status_code", "vType": "INT64", "vInt64": 200},
{"key": "error", "vType": "BOOL", "vBool": false}
],
"logs": [
{
"timestamp": 1680000000050000,
"fields": [
{"key": "event", "vType": "STRING", "vStr": "cache.miss"},
{"key": "cache.key", "vType": "STRING", "vStr": "user:9527"}
]
}
],
"process": {
"serviceName": "user-service",
"tags": [
{"key": "hostname", "vType": "STRING", "vStr": "node-1"},
{"key": "ip", "vType": "STRING", "vStr": "10.0.0.1"}
]
},
"spanKind": "client" // Jaeger 特有字段
}
2.4 Jaeger Span 关键字段说明
| 字段 | 类型 | 说明 | 复盘关注点 |
|---|---|---|---|
traceID |
string | 全局唯一 Trace 标识,通常为 128 位或 64 位十六进制 | 检查是否跨服务一致 |
spanID |
string | 当前 Span 的唯一标识 | 用于关联父子关系 |
references |
array | 引用关系列表,常见类型:CHILD_OF、FOLLOWS_FROM |
确认链路拓扑是否正确 |
startTime / duration |
int64 | 开始时间与持续时长,单位微秒 | 性能分析的核心指标 |
tags |
array | 键值对标签,支持 STRING、INT64、BOOL、FLOAT64 四种类型 | 复盘规范的主要操作对象 |
logs |
array | 时间戳事件列表,每条 log 包含多个 field | 用于记录异常、缓存命中/未命中等 |
process |
object | 进程信息,包含 serviceName 和进程级标签 | 确认服务归属与部署环境 |
spanKind |
string | Jaeger 特有,标识 Span 角色:client、server、producer、consumer | 区分调用方向,辅助定位瓶颈 |
2.5 Span 引用关系与链路拓扑
Jaeger 支持两种引用关系:
- CHILD_OF:父 Span 等待子 Span 完成,典型的同步调用关系(如 HTTP 请求)。
- FOLLOWS_FROM:父 Span 不等待子 Span 完成,典型的异步消息或后台任务关系。
下面的 SVG 展示了两种引用关系在链路拓扑中的区别:
常见陷阱: 在 Jaeger 中,如果异步 Span 使用了
CHILD_OF 而非 FOLLOWS_FROM,会导致 Jaeger UI 显示错误的 Trace 时长(父 Span 会等待异步子 Span 完成,造成不必要的超时假象)。复盘时务必检查引用类型。
2.6 SpanContext 的跨进程传递机制
SpanContext 是分布式追踪的“粘合剂”。Jaeger 通过以下方式在服务间传递 SpanContext:
- HTTP:通过
uber-trace-id请求头传递,格式为{traceID}:{spanID}:{parentSpanID}:{flags}。 - gRPC:通过 gRPC 的 metadata 传递,key 为
uber-trace-id。 - 消息队列:通过消息的 header 或 properties 传递。
复盘检查清单:
✅ 检查所有跨服务调用是否携带了
✅ 检查网关或代理层是否透传了追踪上下文。
✅ 检查异步消息消费者是否正确提取并恢复了 SpanContext。
✅ 检查所有跨服务调用是否携带了
uber-trace-id 头。✅ 检查网关或代理层是否透传了追踪上下文。
✅ 检查异步消息消费者是否正确提取并恢复了 SpanContext。