分布式链路追踪

Jaeger、Zipkin、OpenTelemetry 链路追踪原理,TraceID透传、Span关联与长尾延迟分析。

分布式链路追踪

在微服务调用链中,一次请求可能涉及数十个服务。分布式链路追踪记录了请求的完整路径,让"慢在哪里"一目了然。

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. 最佳实践

  1. 命名规范:Span name 格式统一 操作方法,如 SELECT ordersGET /api/orders
  2. 属性标准化:遵循 OpenTelemetry Semantic Conventions
  3. 避免过度追踪:高并发服务使用采样,防止采集压垮系统
  4. 关联日志:TraceID 写入日志,实现日志与追踪联动
  5. 业务标签:在 Span 中记录业务关键标识(订单ID、用户ID)

总结

组件角色
OpenTelemetry标准协议 + 多语言 SDK
Collector数据采集、处理、导出
Jaeger/Tempo存储与查询后端
Grafana统一可视化

链路追踪让分布式系统的"黑盒"变透明,是定位性能瓶颈和故障的根本手段。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「架构」更多文章

  1. 架构评审与技术债管理
  2. SLA/SLO/SLI 与容量规划
  3. 云原生架构模式