OTel可观测Mock隔离接口设计与契约测试 - 第2节

2.1 从单体观测到分布式追踪:核心问题

在单体应用中,一次请求的完整调用链可以通过单一进程内的日志或堆栈轻松串联。进入微服务与分布式架构后,一次用户请求会穿越多个服务、多个进程、甚至多个数据中心。传统的日志聚合无法高效地将分散在各处的调用片段还原为完整的请求视图。OpenTelemetry 的核心概念正是为了解决这一根本矛盾而设计:通过一套标准化的数据模型与传播机制,让分布式系统中的每个可观测信号都能被统一采集、关联和解读。

核心认知: OTel 的六大核心概念并非孤立存在,它们共同构成了一个“信号关联”体系。Trace 提供调用拓扑,Span 记录单步操作,Metrics 聚合统计特征,Logs 保留事件细节,Baggage 传递业务上下文,Context Propagation 则是一切关联的“粘合剂”。

2.2 Trace(追踪):端到端的请求全景

Trace 代表一个完整的、端到端的请求或事务在分布式系统中的执行路径。它从请求进入系统的边界开始,直到请求处理完成并返回响应为止。一个 Trace 由多个 Span 组成,这些 Span 通过父子关系或因果关系连接,形成一棵有向无环图(DAG)。

Trace 结构:一次用户请求的完整追踪 Trace Root Span:GET /api/order/12345 Span A:Auth Service 验证 Token Span B:Order Service 查询数据库 Span C:DB 执行 SQL 查询 Trace ID 全局唯一,所有 Span 共享同一 Trace ID,通过 Span ID 与 Parent Span ID 构建父子关系

2.3 Span(跨度):追踪的基本工作单元

Span 是 Trace 中最小的、不可再分的工作单元。每个 Span 代表一个具有开始时间和结束时间的操作,例如一次 HTTP 调用、一个数据库查询、或一段业务逻辑的执行。Span 是 OTel 数据模型中信息最丰富的实体。

Span 属性 说明 示例值
Trace ID 所属追踪的唯一标识,16 字节(32 位十六进制) 0af7651916cd43dd8448eb211c80319c
Span ID 当前 Span 的唯一标识,8 字节(16 位十六进制) b7ad6b7169203331
Parent Span ID 父 Span 的 ID,根 Span 此字段为空 a2c4e5f678901234
Name Span 的操作名称,通常为方法名或路由 HTTP GET /api/users
Kind Span 的角色类型:Client、Server、Internal、Producer、Consumer SPAN_KIND_SERVER
Start / End Timestamp 操作的开始与结束时间(纳秒精度) 2025-03-21T10:00:00.123456789Z
Status 操作结果:Unset、Ok、Error StatusCode.ERROR
Attributes 键值对形式的元数据,用于丰富 Span 语义 http.status_code=200
Events Span 生命周期内发生的带时间戳的日志事件 cache.miss
Links 关联到其他 Trace 中的 Span,用于跨追踪关联 TraceID: SpanID
Span 生命周期: 创建 Span → 设置初始属性 → 记录事件 → 执行操作 → 设置状态 → 结束 Span。结束后的 Span 不可再修改,由 Exporter 导出到后端。

2.4 Metrics(指标):聚合的数值观测

Metrics 是对系统行为进行聚合统计的数值数据,用于监控趋势、设置告警和容量规划。与 Trace 的“每个请求都记录”不同,Metrics 通常以固定时间间隔采集聚合值,数据量更小、存储成本更低。

Metric 类型 语义 典型场景
Counter 单调递增的累加值,只增不减 请求总数、错误总数
UpDownCounter 可增可减的累加值 活跃连接数、队列深度
Histogram 对观测值进行分桶统计,记录分布 请求延迟 P50/P95/P99
Gauge 当前瞬时值,可上下波动 CPU 使用率、内存占用
Metrics 采集与聚合流程 应用服务 Meter Provider Metric Reader Exporter 应用通过 Meter 创建 Counter/Histogram 等仪器,Meter Provider 聚合后由 Metric Reader 定期拉取,Exporter 推送至后端 支持 Push(推送)与 Pull(拉取)两种采集模式

2.5 Logs(日志):带时间戳的结构化事件

Logs 是系统运行过程中产生的、带有时间戳的离散事件记录。OTel 对 Logs 的定位是“与 Trace 和 Metrics 关联的结构化事件”,而非传统的纯文本日志。OTel Logs 数据模型支持将日志与 Trace 上下文自动关联,实现“日志即追踪的一部分”。

// OTel LogRecord 核心字段示例(JSON 序列化)
{
  "timestamp": "2025-03-21T10:00:00.123456789Z",
  "observedTimestamp": "2025-03-21T10:00:00.123456789Z",
  "severityNumber": 9,          // SEVERITY_ERROR
  "severityText": "ERROR",
  "body": {
    "stringValue": "数据库连接超时,重试第 3 次"
  },
  "attributes": {
    "db.system": "postgresql",
    "db.operation": "SELECT",
    "retry.count": 3
  },
  "traceId": "0af7651916cd43dd8448eb211c80319c",
  "spanId": "b7ad6b7169203331",
  "resource": {
    "service.name": "order-service",
    "host.name": "pod-abc-123"
  }
}
关键区别: 传统日志是“孤立的文本行”,OTel Logs 是“与 Trace 绑定的结构化事件”。通过 traceId 和 spanId 字段,日志可以直接关联到具体的请求和操作,实现“点击日志跳转到追踪”的闭环体验。

2.6 Baggage(行李):跨服务传递的业务上下文

Baggage 是一种轻量级的键值对上下文传递机制,用于在 Trace 内的所有 Span 之间传递业务相关的元数据。与 Span Attributes 不同,Baggage 会随着 Context Propagation 自动传播到下游服务,而 Attributes 仅存在于当前 Span 所在的服务中。

特性 Baggage Span Attributes
传播范围 跨服务、跨进程自动传播 仅限当前 Span 所在进程
存储位置 Context 对象中,随 W3C Baggage 头传递 Span 对象内部
典型用途 用户 ID、会话 ID、AB 测试分组、租户标识 HTTP 状态码、数据库行数、错误详情
性能影响 每个请求都会序列化到 HTTP 头,需控制大小 仅本地内存操作,无网络开销
最佳实践 仅传递少量、高价值的业务上下文(< 10 对) 可包含大量调试信息
// Baggage 使用示例(Java)
// 在入口服务设置 Baggage
Baggage.current().toBuilder()
    .put("user.id", "user_12345")
    .put("experiment.group", "A")
    .build()
    .makeCurrent();

// 在下游服务读取 Baggage
String userId = Baggage.current().getEntryValue("user.id");
// userId = "user_12345"

2.7 Context Propagation(上下文传播):信号关联的基石

Context Propagation 是 OTel 实现分布式追踪和跨服务上下文传递的核心机制。它确保 Trace ID、Span ID、Baggage 等上下文信息能够在服务调用链中无缝传递,无论底层通信协议是 HTTP、gRPC、消息队列还是其他 RPC 框架。

Context Propagation 跨服务传递流程 服务 A 创建 Span A 注入 Context 到 HTTP 头 服务 B 提取 Context 创建 Span B(父=Span A) 服务 C 提取 Context 创建 Span C(父=Span B) HTTP 请求头中携带的传播协议: traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 baggage: user.id=user_12345,experiment.group=A 支持 W3C Trace Context(traceparent/tracestate)和 W3C Baggage 标准,也兼容 Zipkin B3 等旧协议

2.8 六大核心概念的关系矩阵

下表总结了 Trace、Span、Metrics、Logs、Baggage、Context Propagation 之间的协作关系,帮助理解它们如何在一次请求中协同工作。

概念 数据粒度 传播范围 主要用途 关联方式
Trace 请求级 跨服务 端到端调用拓扑 Trace ID 全局唯一
Span 操作级 单服务/跨服务 单步延迟、错误定位 Trace ID + Span ID + Parent Span ID
Metrics 聚合级 不传播 监控、告警、容量 通过 Attributes 关联到服务/资源
Logs 事件级 不传播 调试、审计、异常详情 通过 traceId/spanId 关联到 Span
Baggage 键值对 跨服务传播 业务上下文传递 随 Context Propagation 自动传递
Context Propagation 机制 跨服务 上下文传递基础设施 Trace ID / Span ID / Baggage 的载体
Mock 隔离测试中的核心启示: 在 Mock 隔离测试中,Context Propagation 是必须被 Mock 的关键行为。如果 Mock 服务无法正确传递 traceparent 和 baggage 头,被测服务的追踪链路就会断裂,导致 Trace 不完整、Baggage 丢失。因此,Mock 接口契约必须显式声明对 W3C Trace Context 和 Baggage 协议的支持,并验证传播的正确性。