分布式追踪基础:Trace、Span、SpanContext 核心概念与 OpenTelemetry 标准
分布式追踪是微服务架构下定位性能瓶颈与故障根因的核心手段。在值班台场景中,一次用户请求可能跨越 10+ 服务节点,没有追踪数据,排查问题如同大海捞针。本章从最基础的三个概念——Trace、Span、SpanContext 入手,并介绍当前业界标准 OpenTelemetry,为后续自助查询接口设计奠定理论基础。
2.1 核心概念:Trace、Span、SpanContext
分布式追踪的数据模型由三个核心元素构成,它们之间的关系可以用一个简单的树形结构来理解:Trace 是一棵完整的请求调用树,Span 是树上的每一个节点,SpanContext 则是节点之间传递的“身份证”。
2.1.1 Trace(追踪)
Trace 代表一个完整的、端到端的请求处理过程。从用户点击按钮、API 网关接收请求开始,到最终响应返回,期间经过的所有服务、数据库、消息队列等操作都属于同一个 Trace。每个 Trace 由全局唯一的 TraceID 标识。
2.1.2 Span(跨度)
Span 是 Trace 中的最小工作单元,描述一次具体的操作,例如“调用用户服务”、“查询数据库”、“发送 MQ 消息”。每个 Span 包含以下关键属性:
- SpanID:当前 Span 的唯一标识。
- ParentSpanID:父 Span 的 ID,用于构建父子层级关系。
- OperationName:操作名称,如
GET /api/user。 - StartTime / EndTime:操作的开始与结束时间戳。
- Tags / Attributes:键值对形式的元数据,如
http.status_code=200。 - Events / Logs:Span 生命周期内的关键事件,如缓存命中、异常抛出。
2.1.3 SpanContext(跨度上下文)
SpanContext 是跨进程传递追踪信息的载体。当服务 A 调用服务 B 时,必须将当前 Trace 的上下文通过 HTTP Header、gRPC Metadata 或消息队列属性传递给下游。SpanContext 主要包含:
- TraceID:全局唯一,贯穿整个请求链路。
- SpanID:当前 Span 的 ID,下游会将其作为 ParentSpanID。
- TraceFlags:追踪标志位,例如是否采样。
- Tracestate:携带供应商特定的追踪元数据。
2.2 三者关系:树形结构模型
Trace、Span、SpanContext 共同构成一棵有向无环图(DAG),通常表现为树形结构。根 Span 代表入口请求,子 Span 代表内部调用。下面这张 SVG 图展示了典型的三层调用链路:
2.3 OpenTelemetry 标准简介
OpenTelemetry(简称 OTel)是 CNCF 旗下的可观测性标准,统一了分布式追踪、指标和日志的数据采集规范。它由 OpenTracing 和 OpenCensus 合并而来,目前已成为业界事实标准。值班台自助查询接口的数据源,强烈建议基于 OTel 标准接入。
2.3.1 OpenTelemetry 核心组件
| 组件 | 职责 | 值班台关联 |
|---|---|---|
| API | 定义 Tracer、Span、SpanContext 等接口规范 | 查询接口需理解 OTel 的 Span 数据结构 |
| SDK | 提供 API 的默认实现,包括采样、导出、上下文传播 | SDK 配置影响数据采集的完整性与延迟 |
| Exporter | 将 Span 数据发送到后端存储(Jaeger、Zipkin、ClickHouse 等) | 自助查询接口直接对接后端存储,而非 Exporter |
| Collector | 接收、处理、转发遥测数据的中间代理 | 值班台可通过 Collector 实现数据过滤与采样 |
| Instrumentation | 自动或手动为框架/库注入追踪代码 | 决定哪些服务操作能被追踪到 |
2.3.2 OTel Span 数据模型(简化)
在 OTel 协议中,一个 Span 被序列化为以下关键字段。值班台查询接口返回的 JSON 结构应与此模型对齐:
{
"traceId": "0af7651916cd43dd8448eb211c80319c",
"spanId": "b7ad6b7169203331",
"parentSpanId": "b7ad6b7169203330",
"name": "HTTP GET /api/user",
"kind": "SPAN_KIND_CLIENT",
"startTimeUnixNano": "1680000000000000000",
"endTimeUnixNano": "1680000000000500000",
"attributes": {
"http.method": "GET",
"http.url": "http://user-service:8080/api/user",
"http.status_code": 200
},
"events": [
{
"timeUnixNano": "1680000000000200000",
"name": "cache.miss",
"attributes": { "cache.key": "user_123" }
}
],
"status": {
"code": "STATUS_CODE_OK",
"message": ""
}
}
traceId 和 spanId 是必选查询参数。parentSpanId 用于构建调用树,attributes 和 events 用于展示详细上下文。如果后端存储不支持 OTel 原生格式,需要在接口层做字段映射。
2.4 上下文传播机制
SpanContext 的跨服务传递是分布式追踪能够串联完整链路的关键。OTel 标准定义了两种传播方式:
- W3C TraceContext(推荐):使用 HTTP Header
traceparent和tracestate。格式为00-{traceId}-{spanId}-{traceFlags}。 - B3 Propagation:Zipkin 使用的传播格式,通过
X-B3-TraceId、X-B3-SpanId等 Header 传递。
下面这张 SVG 图展示了 SpanContext 在服务间传递的流程:
2.5 值班台场景中的关键设计要点
- TraceID 索引:后端存储必须为 TraceID 建立倒排索引,确保毫秒级查询。
- Span 父子关系重建:查询接口返回的 Span 列表需按
parentSpanId组织成树形结构,方便前端渲染。 - 采样策略透明:接口应返回采样率信息,避免用户因数据缺失误判。
- 上下文传播完整性:如果发现大量孤儿 Span(无父 Span),说明传播配置有误,需告警。