2. Jaeger 调用链数据模型:理解核心数据结构
在构建 SLI 口径基线校准之前,必须深入理解 Jaeger 的数据模型。Jaeger 并非直接存储原始请求,而是将分布式调用抽象为 Span、Trace、ServiceName、OperationName 等结构化对象。这些数据结构是后续定义 SLI 指标、过滤异常、聚合延迟的原子单元。
2.1 核心数据结构全景
Jaeger 的数据模型由四个核心实体构成,它们之间的关系可以用下面的结构图清晰展示:
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 聚合 |
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 要么全部采样,要么全部丢弃。
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:特定服务的特定操作 |
/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
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 之间的完整关系:
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 |