2. Jaeger 调用链数据模型:理解核心数据结构

在构建 SLI 口径基线校准之前,必须深入理解 Jaeger 的数据模型。Jaeger 并非直接存储原始请求,而是将分布式调用抽象为 Span、Trace、ServiceName、OperationName 等结构化对象。这些数据结构是后续定义 SLI 指标、过滤异常、聚合延迟的原子单元。

2.1 核心数据结构全景

Jaeger 的数据模型由四个核心实体构成,它们之间的关系可以用下面的结构图清晰展示:

Jaeger 核心数据模型结构 Trace(调用链) Span A ServiceName: frontend OperationName: /checkout Duration: 120ms Tags: http.status=200 Span B ServiceName: payment OperationName: charge Duration: 80ms Tags: error=true Span C ServiceName: inventory OperationName: reserve Duration: 50ms Tags: db.query=ok 关键关系说明 • Trace 由一组具有相同 TraceID 的 Span 组成,代表一次完整的请求路径。 • 每个 Span 记录一个服务中的一个操作,包含 ServiceName + OperationName 唯一标识。 • Tags 携带键值对元数据,Logs 记录时间戳事件,Process 描述 Span 所属进程信息。 • Span 通过 SpanContext(TraceID + SpanID + ParentSpanID)建立父子或兄弟关系。

2.2 Span:调用链的原子单元

Span 是 Jaeger 中最基本的数据单元,代表分布式系统中一个服务内的一个操作片段。每个 Span 包含以下关键字段:

字段 类型 说明 SLI 校准用途
TraceID string (hex) 全局唯一调用链标识,所有 Span 共享 关联同一请求的所有 Span,用于端到端延迟计算
SpanID string (hex) 当前 Span 的唯一标识 定位单个操作,用于错误率统计
ParentSpanID string (hex) 父 Span 的 ID,根 Span 为空 构建调用拓扑,用于依赖分析
OperationName string 操作名称,如 /api/checkout SLI 分组维度,按操作聚合延迟/错误率
StartTime int64 (μs) Span 开始时间戳 计算响应时间、时间窗口对齐
Duration int64 (μs) Span 持续时长 延迟 SLI 的核心指标值
Tags map[string]interface{} 键值对元数据,如 http.status_code 过滤条件(如只统计 200 状态码)
Logs []Log 时间戳事件列表,记录异常或关键点 错误详情分析,辅助 SLI 异常归因
Process Process 服务进程信息,包含 ServiceName 服务级 SLI 聚合
SLI 校准要点: 在基线校准中,Span 的 Duration 字段直接作为延迟 SLI 的原始数据点;Tags 中的 error=true 或 http.status_code >= 500 用于标记错误 SLI。务必确保 Span 的 StartTime 和 Duration 精度满足微秒级要求,否则基线聚合会产生偏差。

2.3 Trace:完整的请求生命周期

Trace 是由一组具有相同 TraceID 的 Span 构成的树形结构,代表一次完整的分布式请求。Trace 的根 Span 没有父 Span,其 Duration 通常代表端到端响应时间。

Trace 的核心特征:

  • 唯一标识: TraceID 是 128 位或 64 位十六进制字符串,在请求入口生成,透传至所有下游服务。
  • 树形结构: Span 通过 ParentSpanID 形成父子关系,根 Span 是树的根节点。
  • 完整性: 一个 Trace 包含从入口到所有下游服务的完整调用路径,是 SLI 端到端延迟计算的依据。
  • 采样关联: Jaeger 通过 TraceID 进行采样决策,同一 Trace 的所有 Span 要么全部采样,要么全部丢弃。
基线校准场景: 当校准“下单接口 P99 延迟”时,需要提取所有 TraceID 对应的根 Span Duration,按 OperationName=/checkout 过滤,然后计算 P99 分位数。如果采样率过低,基线会失真;建议在基线校准期间将目标服务的采样率临时提升至 100%。

2.4 ServiceName 与 OperationName:SLI 的聚合维度

ServiceName 和 OperationName 是 Jaeger 数据模型中最重要的两个分组维度,直接决定 SLI 指标的聚合粒度。

维度 来源 示例值 SLI 聚合粒度
ServiceName Span.Process.ServiceName frontend, payment, inventory 服务级 SLI:如 frontend 服务的错误率
OperationName Span.OperationName /checkout, charge, reserve 操作级 SLI:如 /checkout 接口的 P99 延迟
ServiceName + OperationName 组合 frontend:/checkout 最细粒度 SLI:特定服务的特定操作
常见陷阱: OperationName 如果包含动态参数(如 /user/12345),会导致基数爆炸,使 SLI 聚合失效。建议在 SDK 端使用模板化 OperationName(如 /user/{id}),或在 Jaeger 查询时使用正则过滤。

2.5 Tags:可扩展的元数据

Tags 是 Span 的键值对元数据,用于携带请求上下文、业务属性和技术指标。Jaeger 官方定义了一组语义约定(Semantic Conventions),但用户也可以自定义 Tags。

常用 Tags 分类:

  • HTTP 标签: http.method, http.url, http.status_code, http.host
  • 数据库标签: db.type, db.instance, db.statement
  • 错误标签: error (bool), error.message, error.stack
  • 业务标签: user.id, order.id, payment.amount

Tags 在 SLI 基线校准中的典型用法:

# 查询所有错误 Span(用于错误率 SLI)
Tags.error = true

# 查询 HTTP 500 响应(用于可用性 SLI)
Tags.http.status_code >= 500

# 查询特定业务订单的延迟(用于业务 SLI)
Tags.order.id = "ORD-2024-001"

2.6 Logs:时间戳事件记录

Logs 是 Span 内的时间戳事件列表,每个 Log 包含一个时间戳和一组键值对。与 Tags 不同,Logs 用于记录 Span 生命周期内的离散事件,而非 Span 的固定属性。

Logs 的典型场景:

  • 异常记录: 当捕获到异常时,记录 event=exception, error.object, stack
  • 关键节点: 记录 event=cache.hit, event=db.query.start
  • 业务事件: 记录 event=payment.success, event=inventory.low
SLI 校准辅助: Logs 不直接参与 SLI 指标计算,但可用于异常归因。例如,当延迟 SLI 出现异常尖刺时,可以查看对应 Span 的 Logs 中是否有 event=retry 或 event=timeout 事件,帮助判断根因。

2.7 Process:服务进程信息

Process 描述 Span 所属的服务进程,包含以下字段:

字段 说明 示例
ServiceName 服务名称,SLI 聚合的关键维度 frontend
Tags 进程级标签,如 hostname, ip, version hostname=web-01, version=1.2.3

Process 信息在 Jaeger 存储中通常被压缩存储,多个 Span 共享同一个 Process 对象,以减少存储开销。在 SLI 基线校准时,可以通过 Process.Tags 中的 version 标签区分不同版本的基线,实现版本级 SLI 对比。

2.8 数据模型关系总结

下面的 SVG 图展示了 Span、Trace、ServiceName、OperationName、Tags、Logs、Process 之间的完整关系:

Jaeger 数据模型实体关系图 Trace Span ServiceName OperationName Tags Logs Process 包含 1..N 所属服务 操作名称 元数据 事件记录 进程信息

2.9 数据模型在 SLI 基线校准中的映射

理解数据模型后,可以建立从 Jaeger 字段到 SLI 指标的清晰映射:

SLI 类型 Jaeger 数据源 过滤条件 聚合函数
延迟 SLI Span.Duration ServiceName + OperationName P50, P90, P99, Avg, Max
错误率 SLI Span.Tags.error Tags.error = true 或 http.status_code >= 500 Count(error) / Count(total)
吞吐量 SLI Span.StartTime 按时间窗口分组 Count(Span) / 时间窗口
可用性 SLI Span.Tags.http.status_code 排除 5xx 或超时 Span Count(success) / Count(total)
饱和度 SLI Span.Tags (自定义) 如 queue.depth, cpu.usage Avg, Max
实战建议: 在 Jaeger 中查询 SLI 基线数据时,推荐使用 Jaeger 的 gRPC 查询接口(jaeger-query)或 OpenTelemetry Collector 的 spanmetrics 处理器。避免直接查询原始 Span 数据做实时聚合,应使用预聚合的指标数据(如 spanmetrics 生成的 RED 指标)来降低查询延迟和存储压力。