2、分布式追踪基础:分布式追踪的概念、Trace/Span/Context Propagation核心术语、OpenTelemetry标准介绍

2.1 分布式追踪:从“黑盒”到“白盒”的跨越

在单体应用时代,一次用户请求的生命周期被限制在单一进程内。通过日志和单机 profiling 工具,工程师可以相对容易地定位性能瓶颈。然而,当应用拆分为数十甚至数百个微服务后,一个请求会穿越多个服务、数据库、消息队列和第三方 API。传统的监控手段(如单机 CPU、内存指标)只能告诉你“系统出问题了”,却无法回答“是哪个服务、哪段代码拖慢了整个请求”。

分布式追踪(Distributed Tracing)正是为解决这一难题而生。它通过为每个请求注入一个全局唯一的 Trace ID,并记录请求在跨服务调用链中每个环节的耗时与状态,从而将一次分布式请求的完整路径还原为一条可视化的“调用链”。

核心价值: 分布式追踪让工程师能够从“黑盒”式的指标告警,升级为“白盒”式的请求级诊断,快速定位跨服务延迟、错误传播和依赖瓶颈。
分布式追踪概念对比:单体 vs 微服务请求路径 单体应用 用户请求 单一进程 日志 / Profiling 容易定位 微服务 + 分布式追踪 服务 A 服务 B 服务 C 服务 D Span A Span B Span C Span D Trace ID: abc-123 贯穿所有服务

2.2 核心术语:Trace、Span 与 Context Propagation

理解分布式追踪,必须掌握三个核心概念:Trace、Span 和 Context Propagation。它们构成了追踪数据的骨架与灵魂。

2.2.1 Trace(追踪)

Trace 代表一次完整的请求或事务在分布式系统中的全生命周期。它由一个全局唯一的 Trace ID 标识。一个 Trace 由一组具有因果关系的 Span 组成,这些 Span 共同描述了请求从入口到出口的完整路径。

2.2.2 Span(跨度)

Span 是追踪的最小工作单元,代表系统中一个有名称、有开始时间和结束时间的操作。每个 Span 包含以下关键属性:

  • Span ID:当前 Span 的唯一标识。
  • Parent Span ID:父 Span 的 ID,用于构建 Span 之间的父子关系(树形结构)。
  • Trace ID:所属 Trace 的 ID。
  • Operation Name:操作名称,例如 HTTP GET /api/users 或 SELECT * FROM orders。
  • Start / End Timestamp:操作的开始和结束时间。
  • Attributes:键值对形式的元数据,用于附加业务或技术信息(如 HTTP 状态码、数据库语句)。
  • Events:带时间戳的日志事件,用于记录 Span 生命周期内的关键点(如异常、缓存命中)。
  • Status:操作结果状态(OK / ERROR / UNSET)。

2.2.3 Context Propagation(上下文传播)

Context Propagation 是分布式追踪的“神经系统”。它负责将 Trace ID 和 Span ID 等信息,随着请求从一个服务传递到下一个服务。常见的传播方式包括:

  • HTTP Headers:通过 traceparent、tracestate 等标准 Header 传递。
  • 消息队列 Headers:在 Kafka、RabbitMQ 等消息的元数据中注入。
  • gRPC Metadata:通过 gRPC 的 Metadata 机制传递。
实践要点: 上下文传播必须由框架或 SDK 自动完成,避免业务代码手动传递。如果传播中断,Trace 就会断裂,无法形成完整的调用链。
Trace / Span 树形结构与上下文传播 Trace / Span 树形结构与上下文传播 Root Span Trace ID: abc-123 Span A: 服务A Span B: 服务B Span C: 服务C Span A1: DB查询 Span B1: 缓存 Span C1: 外部API Context Propagation 流程 1. 服务A 创建 Span A,通过 HTTP Header (traceparent) 将 Trace ID + Span ID 传给 服务B 2. 服务B 接收 Header,创建 Span B,并设置 Parent Span ID = Span A 的 ID

2.3 核心术语对比表

术语 英文 定义 类比(现实世界)
追踪 Trace 一次完整请求的端到端路径,由 Trace ID 唯一标识 一次快递从寄件到签收的完整物流轨迹
跨度 Span 追踪中的单个工作单元,记录一个操作的开始、结束和元数据 物流轨迹中的每一个节点(揽收、分拣、运输、派送)
上下文传播 Context Propagation 将 Trace ID 和 Span ID 等信息跨服务边界传递的机制 快递单号在物流各环节之间的传递
跨度 ID Span ID 每个 Span 的唯一标识 每个物流节点的唯一编号
父跨度 ID Parent Span ID 标识当前 Span 的父 Span,用于构建树形结构 当前物流节点的上一个节点编号

2.4 OpenTelemetry 标准:统一可观测性的“普通话”

在 OpenTelemetry(简称 OTel)出现之前,分布式追踪领域存在多种标准(如 OpenTracing、OpenCensus、Jaeger、Zipkin 等),导致厂商锁定和集成成本高昂。OpenTelemetry 由 CNCF 孵化,旨在提供一套厂商中立、统一的 API、SDK 和协议,用于生成、采集和导出遥测数据(Traces、Metrics、Logs)。

2.4.1 OpenTelemetry 核心组件

  • API:定义 Traces、Metrics、Logs 的数据模型和操作接口,与具体实现解耦。
  • SDK:提供 API 的默认实现,包括采样、处理、导出等能力。
  • Collector:一个独立的代理服务,用于接收、处理、聚合和导出遥测数据。支持多种接收器(Receiver)和导出器(Exporter)。
  • Exporter:将遥测数据发送到后端存储或分析平台(如 Jaeger、Prometheus、Datadog、Grafana Tempo)。
  • Instrumentation Libraries:为常见框架和库(如 Spring Boot、Express、gRPC、Redis)提供自动埋点能力,无需手动修改业务代码。

2.4.2 OpenTelemetry 的优势

特性 说明
厂商中立 一次接入,可切换任意后端(Jaeger、Zipkin、Datadog、Grafana 等)
统一标准 融合 OpenTracing 和 OpenCensus,避免重复投资
多信号支持 同时支持 Traces、Metrics、Logs,实现真正的可观测性三大支柱
自动埋点 通过 Instrumentation Libraries 自动注入,降低接入成本
活跃社区 CNCF 孵化项目,社区贡献活跃,生态丰富

2.4.3 OpenTelemetry 数据模型示例(Trace)

以下是一个使用 OpenTelemetry API 创建 Span 的代码示例(Java 语言):

// 1. 获取 Tracer
Tracer tracer = openTelemetry.getTracer("com.example.service");

// 2. 创建一个 Span(自动成为当前 Span 的子 Span)
Span span = tracer.spanBuilder("processOrder")
    .setAttribute("order.id", "12345")
    .setAttribute("payment.method", "credit_card")
    .startSpan();

// 3. 将 Span 放入当前上下文
try (Scope scope = span.makeCurrent()) {
    // 执行业务逻辑
    Thread.sleep(100);
    // 记录事件
    span.addEvent("payment.processed");
} catch (Exception e) {
    span.setStatus(StatusCode.ERROR, "Payment failed: " + e.getMessage());
    span.recordException(e);
} finally {
    // 4. 结束 Span
    span.end();
}
常见误区: 不要忘记在 finally 块中调用 span.end(),否则 Span 将永远不会被导出,导致追踪数据丢失。使用 try-with-resources 或 Scope 模式可以自动管理 Span 的生命周期。

2.5 本章小结