分布式链路追踪

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

分布式链路追踪

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

为什么需要分布式追踪

仅用日志和指标排查分布式系统的问题,往往遇到以下困境:

  1. 日志淹没:每秒数万条日志,哪些属于同一次请求?grep 无法关联跨服务的日志
  2. 指标盲区:Prometheus 告诉你 API 平均延迟 200ms,但 P99 达到 3 秒——慢请求发生在哪个服务?
  3. 上下文丢失:网关日志显示请求成功,但用户反馈页面空白——中间哪个服务静默失败了?

分布式追踪通过在请求流经每个服务时创建 Span,将分散的调用串联成一棵完整的调用树,使得上述问题迎刃而解。

日志 vs 指标 vs 追踪:

维度日志(Logging)指标(Metrics)追踪(Tracing)
数据粒度离散事件聚合数值请求级时序
用途调试细节趋势监控根因定位
存储成本高低中
查询方式文本搜索时序查询TraceID/标签过滤
三者关系“发生了什么”“整体趋势如何”“在哪里花了时间”

最佳实践:三种可观测性信号不应孤立使用。在 Grafana 中将 Metrics 仪表盘与 Traces 关联——点击某个延迟峰值点直接跳转到对应的 Trace 详情。

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 过长
   - 数据库慢查询
   - 下游服务超时重试
   - 网络抖动

根因定位流程:

  1. 筛选慢 Trace:按 duration > 1s 过滤,关注 P99 以上的异常值
  2. 对比分析:将慢请求的正常 Trace 与异常 Trace 并排对比,定位差异 Span
  3. 关键路径分析:Jaeger 自动计算 Critical Path,识别最长耗时链路
  4. 关联日志:点击 Span 中的日志事件,跳转到 Elasticsearch/Loki 查看详细日志
  5. 多维归因:按 service.name、host.name、http.status_code 分组,定位问题集群或实例

7. 最佳实践

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

总结

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

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

继续阅读

探索更多技术文章

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

全部文章 返回首页

「架构」更多文章

  1. 韧性工程与错误预算:从 SLO 到故障演练
  2. 微前端架构:组合、隔离与独立部署
  3. 数据网格(Data Mesh):领域数据产品与去中心化治理