OTel可观测Mock隔离接口设计与契约测试 - 第2节
2.1 从单体观测到分布式追踪:核心问题
在单体应用中,一次请求的完整调用链可以通过单一进程内的日志或堆栈轻松串联。进入微服务与分布式架构后,一次用户请求会穿越多个服务、多个进程、甚至多个数据中心。传统的日志聚合无法高效地将分散在各处的调用片段还原为完整的请求视图。OpenTelemetry 的核心概念正是为了解决这一根本矛盾而设计:通过一套标准化的数据模型与传播机制,让分布式系统中的每个可观测信号都能被统一采集、关联和解读。
2.2 Trace(追踪):端到端的请求全景
Trace 代表一个完整的、端到端的请求或事务在分布式系统中的执行路径。它从请求进入系统的边界开始,直到请求处理完成并返回响应为止。一个 Trace 由多个 Span 组成,这些 Span 通过父子关系或因果关系连接,形成一棵有向无环图(DAG)。
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 |
2.4 Metrics(指标):聚合的数值观测
Metrics 是对系统行为进行聚合统计的数值数据,用于监控趋势、设置告警和容量规划。与 Trace 的“每个请求都记录”不同,Metrics 通常以固定时间间隔采集聚合值,数据量更小、存储成本更低。
| Metric 类型 | 语义 | 典型场景 |
|---|---|---|
| Counter | 单调递增的累加值,只增不减 | 请求总数、错误总数 |
| UpDownCounter | 可增可减的累加值 | 活跃连接数、队列深度 |
| Histogram | 对观测值进行分桶统计,记录分布 | 请求延迟 P50/P95/P99 |
| Gauge | 当前瞬时值,可上下波动 | CPU 使用率、内存占用 |
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"
}
}
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 框架。
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 的载体 |