分布式链路追踪
在微服务调用链中,一次请求可能涉及数十个服务。分布式链路追踪记录了请求的完整路径,让"慢在哪里"一目了然。
1. 核心概念
Trace:一次完整请求
└── Span A (Gateway) [0ms ───── 5ms]
└── Span B (Order) [2ms ───── 15ms]
├── Span C (DB) [3ms ─ 8ms]
└── Span D (Stock)[10ms ─ 14ms]
└── Span E (RPC)[11ms ─ 13ms]
| 概念 | 说明 |
|---|---|
| Trace | 一次端到端请求的完整链路,由唯一 TraceID 标识 |
| Span | 链路中的一个操作单元,包含起止时间、标签、日志 |
| SpanContext | 跨进程传递的上下文(TraceID + SpanID + flags) |
| Baggage | 随链路传播的自定义键值对 |
2. 数据模型(OpenTelemetry)
Span:
- trace_id: 16 bytes
- span_id: 8 bytes
- parent_span_id: 8 bytes (根 Span 为空)
- name: "GET /api/orders"
- kind: SERVER / CLIENT / PRODUCER / CONSUMER / INTERNAL
- start_time, end_time
- attributes: { "http.method": "GET", "http.status_code": 200 }
- events: [ { timestamp, name, attributes } ]
- status: UNSET / OK / ERROR
3. TraceID 透传机制
HTTP Header 传递
请求入站:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
格式: {version}-{trace-id}-{parent-id}-{trace-flags}
00 4bf9...4736 00f0...02b7 01
代码示例(OpenTelemetry Java)
// 自动埋点(Spring Boot)
implementation 'io.opentelemetry:opentelemetry-spring-boot-starter'
// 手动创建 Span
Tracer tracer = openTelemetry.getTracer("order-service");
Span span = tracer.spanBuilder("processOrder")
.setSpanKind(SpanKind.SERVER)
.startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("order.id", orderId);
span.setAttribute("user.id", userId);
// 业务逻辑...
span.addEvent("validation.completed");
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
throw e;
} finally {
span.end();
}
跨进程传播
// 服务端提取上下文
Context extracted = propagator.extract(Context.current(), headers, getter);
// 客户端注入上下文
propagator.inject(context, requestBuilder, setter);
4. 采集与存储
架构
Application → SDK/Agent → Collector → Backend → UI
(OTLP) (Jaeger/Tempo)
┌─────────────────────┐
│ OpenTelemetry │
│ Collector │
│ receivers → processors → exporters
└─────────────────────┘
采样策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 头部采样 | 在请求入口处决定是否采样 | 简单、低开销 |
| 尾部采样 | 收集后根据完整链路特征决定 | 仅保留错误/慢请求 |
| 概率采样 | 固定比例采样 | 通用场景 |
| 限速采样 | 限制单位时间采样数 | 高流量服务 |
# Collector 尾部采样配置
tail_sampling:
policies:
- name: errors
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow
type: latency
latency: { threshold_ms: 1000 }
5. Jaeger 实战
部署
# Docker Compose 示例
services:
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # UI
- "14268:14268" # Collector HTTP
environment:
COLLECTOR_OTLP_ENABLED: true
查询分析
Jaeger UI 功能:
- Search:按服务、标签、时间范围检索 Trace
- Compare:对比两次请求的链路差异
- Dependencies:服务依赖拓扑图
- Trace View:瀑布图展示 Span 时序
- Critical Path:识别最长路径
与 Prometheus/Grafana 联动
# Grafana 数据源配置
- name: Jaeger
type: jaeger
url: http://jaeger-query:16686
# 在 Grafana 中关联 Metrics → Traces
# 点击延迟峰值直接跳转到对应 Trace
6. 长尾延迟分析
P99 延迟高 ≠ 所有请求都慢。分析长尾请求:
1. 按 Trace 中最大 Span 耗时排序
2. 识别 Critical Path 上的瓶颈
3. 对比正常 vs 异常 Trace 的差异
4. 常见根因:
- 某实例 GC STW 过长
- 数据库慢查询
- 下游服务超时重试
- 网络抖动
7. 最佳实践
- 命名规范:Span name 格式统一
操作方法,如SELECT orders或GET /api/orders - 属性标准化:遵循 OpenTelemetry Semantic Conventions
- 避免过度追踪:高并发服务使用采样,防止采集压垮系统
- 关联日志:TraceID 写入日志,实现日志与追踪联动
- 业务标签:在 Span 中记录业务关键标识(订单ID、用户ID)
总结
| 组件 | 角色 |
|---|---|
| OpenTelemetry | 标准协议 + 多语言 SDK |
| Collector | 数据采集、处理、导出 |
| Jaeger/Tempo | 存储与查询后端 |
| Grafana | 统一可视化 |
链路追踪让分布式系统的"黑盒"变透明,是定位性能瓶颈和故障的根本手段。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。