分布式追踪核心概念

分布式追踪核心概念

什么是分布式追踪

在微服务架构中,一个用户请求可能经过十几个甚至上百个服务。分布式追踪(Distributed Tracing)用于跟踪和分析请求在各个服务间的完整调用链路。

为什么需要分布式追踪

传统单体应用

用户请求 → 应用服务器 → 数据库
         ↓
      日志文件(容易定位问题)

微服务架构

用户请求
   ↓
API Gateway (10ms)
   ├→ 用户服务 (50ms)
   ├→ 订单服务 (2000ms) ← 性能瓶颈!
   │   ├→ 库存服务 (100ms)
   │   └→ 支付服务 (1800ms) ← 真正的问题!
   ├→ 推荐服务 (30ms)
   └→ 通知服务 (20ms)

问题

  • ❌ 用户反馈"页面很慢",但不知道哪个服务慢
  • ❌ 查看日志需要跨多个服务、多个实例
  • ❌ 无法了解服务间的依赖关系
  • ❌ 难以定位级联故障的根因

分布式追踪的价值

  • 可视化调用链路:一目了然看到完整请求路径
  • 定位性能瓶颈:快速找到慢的服务和方法
  • 分析依赖关系:了解服务拓扑和调用关系
  • 故障诊断:快速定位错误发生的位置

核心概念

1. Trace(追踪)

Trace 表示一次完整的请求调用链,包含多个 Span。

Trace ID: abc123456789
Duration: 2190ms
Services: 5
Spans: 12

用户请求 /order/create
   ├─ API Gateway
   ├─ 订单服务
   │   ├─ 数据库查询
   │   └─ 库存检查
   ├─ 支付服务
   │   └─ 第三方支付接口
   └─ 通知服务

属性

  • Trace ID:全局唯一标识符
  • Duration:总耗时
  • Start Time:开始时间
  • Services:涉及的服务数量
  • Status:成功/失败

2. Span(跨度)

Span 是 Trace 中的一个操作单元,代表一个服务中的一段处理过程。

Span:
  Trace ID: abc123456789
  Span ID: span-001
  Parent Span ID: null
  Service: order-service
  Operation: POST /api/orders
  Start Time: 2024-01-09 10:00:00.000
  Duration: 200ms
  Status: OK
  
  Tags:
    http.method: POST
    http.url: /api/orders
    http.status_code: 200
    user.id: 12345
    
  Logs:
    - timestamp: 10:00:00.050
      event: "db.query.start"
      message: "SELECT * FROM products"
    - timestamp: 10:00:00.150
      event: "db.query.end"
      rows: 5

Span 属性

  • Span ID:Span 的唯一标识
  • Parent Span ID:父 Span ID(构建调用树)
  • Operation Name:操作名称
  • Start/End Time:开始和结束时间
  • Duration:耗时
  • Tags:键值对元数据
  • Logs:时间点事件

3. Tags(标签)

Tags 是 Span 的元数据,用于查询和过滤。

常用标签

# HTTP 相关
http.method: GET
http.url: /api/users/123
http.status_code: 200

# 数据库相关
db.type: mysql
db.instance: users_db
db.statement: SELECT * FROM users WHERE id=?

# 消息队列
message_bus.destination: order_queue
message_bus.message_id: msg-123

# 错误相关
error: true
error.kind: DatabaseException
error.message: "Connection timeout"

# 业务相关
user.id: 12345
order.id: ord-98765
payment.method: credit_card

4. Logs(日志)

Logs 是 Span 内的时间点事件,记录关键操作。

{
  "timestamp": "2024-01-09T10:00:00.050Z",
  "event": "cache.miss",
  "fields": {
    "key": "user:12345",
    "reason": "key_not_found"
  }
}

与传统日志的区别

维度 传统日志 Tracing Logs
关联性 独立的日志行 关联到 Span
上下文 有限 完整的 Trace 上下文
查询 文本搜索 结构化查询
聚合 困难 自动聚合

5. Context Propagation(上下文传播)

上下文传播是分布式追踪的核心机制,通过 HTTP Header、消息队列 Header 等传递 Trace 信息。

HTTP Header 传播

Service A → Service B

Request Headers:
  X-B3-TraceId: abc123456789
  X-B3-SpanId: span-001
  X-B3-ParentSpanId: span-000
  X-B3-Sampled: 1

常见格式

  • B3 Format(Zipkin):X-B3-TraceId, X-B3-SpanId
  • W3C Trace Contexttraceparent, tracestate
  • Jaeger Formatuber-trace-id

代码示例

// Service A 发起请求
func callServiceB(ctx context.Context) {
    // 1. 从上下文提取 Trace 信息
    span := opentracing.SpanFromContext(ctx)
    
    // 2. 创建 HTTP 请求
    req, _ := http.NewRequest("GET", "http://service-b/api", nil)
    
    // 3. 注入 Trace 信息到 Header
    opentracing.GlobalTracer().Inject(
        span.Context(),
        opentracing.HTTPHeaders,
        opentracing.HTTPHeadersCarrier(req.Header),
    )
    
    // 4. 发送请求
    resp, _ := http.DefaultClient.Do(req)
}

// Service B 接收请求
func handleRequest(w http.ResponseWriter, r *http.Request) {
    // 1. 从 Header 提取 Trace 信息
    spanCtx, _ := opentracing.GlobalTracer().Extract(
        opentracing.HTTPHeaders,
        opentracing.HTTPHeadersCarrier(r.Header),
    )
    
    // 2. 创建新的 Span(作为子 Span)
    span := opentracing.StartSpan(
        "handle_request",
        opentracing.ChildOf(spanCtx),
    )
    defer span.Finish()
    
    // 3. 处理业务逻辑
    processRequest(r)
}

追踪系统架构

标准架构

┌─────────────────────────────────────────────┐
│          Application Layer                  │
│  ┌─────────────────────────────────────┐   │
│  │  Application Code                   │   │
│  │  + Tracing Library (SDK)            │   │
│  └──────────────┬──────────────────────┘   │
└────────────────┼────────────────────────────┘
                 │ 发送 Spans
                 ▼
┌─────────────────────────────────────────────┐
│          Collection Layer                   │
│  ┌──────────────┐  ┌──────────────┐         │
│  │ Agent/Sidecar│  │  Collector   │         │
│  │ (可选)        │  │  (聚合、验证) │         │
│  └──────┬───────┘  └──────┬───────┘         │
└─────────┼──────────────────┼─────────────────┘
          │                  │
          ▼                  ▼
┌─────────────────────────────────────────────┐
│          Storage Layer                      │
│  ┌──────────────────────────────────────┐   │
│  │  Storage Backend                     │   │
│  │  - ElasticSearch                     │   │
│  │  - Cassandra                         │   │
│  │  - Memory (开发)                      │   │
│  └──────────────┬───────────────────────┘   │
└─────────────────┼───────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────┐
│          Query & UI Layer                   │
│  ┌──────────────────────────────────────┐   │
│  │  Query Service + Web UI              │   │
│  │  - 搜索 Traces                        │   │
│  │  - 可视化调用链                        │   │
│  │  - 性能分析                           │   │
│  └──────────────────────────────────────┘   │
└─────────────────────────────────────────────┘

采样策略

为了降低性能开销,通常只采样部分请求。

1. 固定比例采样

// 10% 采样率
sampler := jaeger.NewConstSampler(0.1)

2. 速率限制采样

// 每秒最多 100 个 Traces
sampler := jaeger.NewRateLimitingSampler(100)

3. 自适应采样

// 根据负载动态调整
sampler := jaeger.NewAdaptiveSampler(
    jaeger.SamplingStrategyResponse{
        OperationSampling: &jaeger.PerOperationSamplingStrategies{
            DefaultSamplingProbability: 0.1,
            PerOperationStrategies: []jaeger.OperationSamplingStrategy{
                {
                    Operation:             "POST /api/orders",
                    ProbabilisticSampling: &jaeger.ProbabilisticSamplingStrategy{
                        SamplingRate: 1.0,  // 重要接口 100% 采样
                    },
                },
            },
        },
    },
)

4. 错误优先采样

// 所有错误请求都采样
if span.Error() {
    span.SetTag(ext.SamplingPriority, 1)  // 强制采样
}

关键指标

1. 延迟(Latency)

P50: 100ms   (中位数)
P90: 250ms   (90% 请求在此之内)
P95: 400ms   (95% 请求在此之内)
P99: 800ms   (99% 请求在此之内)
Max: 2000ms  (最大延迟)

2. 错误率(Error Rate)

Total Requests: 10,000
Failed Requests: 50
Error Rate: 0.5%

3. 吞吐量(Throughput)

Requests Per Second (RPS): 500
Traces Per Second (TPS): 450  (采样率 90%)

4. 服务依赖

服务拓扑图:
  API Gateway
     ├→ User Service (100% 依赖)
     ├→ Order Service (80% 依赖)
     │   ├→ Inventory Service
     │   └→ Payment Service
     └→ Notification Service (50% 依赖)

与日志、监控的关系

可观测性三大支柱

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  Metrics    │  │   Logging   │  │   Tracing   │
│  (指标)      │  │   (日志)     │  │   (追踪)     │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────────────────┴────────────────┘
                        │
              完整的可观测性平台

各自的作用

维度 Metrics Logging Tracing
回答问题 系统健康吗? 发生了什么? 为什么慢?
数据类型 数值 文本事件 调用图
存储成本
查询速度
聚合 容易 困难 中等

联动使用

# 1. Metrics 发现问题
Alert: API响应时间 P99 > 1s

# 2. Tracing 定位具体慢的请求
Trace ID: abc123
Duration: 2.5s
问题服务: Payment Service

# 3. Logging 查看详细错误
Payment Service Logs:
  [ERROR] Database connection timeout
  Connection pool exhausted

OpenTelemetry 标准

OpenTelemetry 是 CNCF 的可观测性标准,统一了 Tracing、Metrics、Logging。

核心组件

┌─────────────────────────────────────┐
│  OpenTelemetry SDK                  │
│  - Trace API                        │
│  - Metrics API                      │
│  - Logs API                         │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│  OpenTelemetry Collector            │
│  - 接收                              │
│  - 处理                              │
│  - 导出                              │
└──────────────┬──────────────────────┘
               │
        ┌──────┴──────┬──────────┐
        ▼             ▼          ▼
   ┌────────┐   ┌────────┐  ┌────────┐
   │ Jaeger │   │Prometheus│ │  ELK   │
   └────────┘   └────────┘  └────────┘

优势

厂商中立:不绑定特定实现
统一 API:一套代码支持多个后端
标准化:W3C Trace Context 标准
功能全面:Trace + Metrics + Logs

总结

核心概念回顾

  1. Trace:完整的请求调用链
  2. Span:单个服务的处理单元
  3. Context Propagation:跨服务传递 Trace 信息
  4. Sampling:降低性能开销
  5. Tags & Logs:丰富上下文信息

何时使用分布式追踪

适合场景
✅ 微服务架构(> 5 个服务)
✅ 性能问题频繁
✅ 服务依赖复杂
✅ 需要故障定位

不太需要
❌ 单体应用
❌ 服务数量少(< 3 个)
❌ 性能稳定
❌ 简单的调用链

实施建议

第一阶段

  • 选择 Jaeger/Zipkin
  • 在关键服务埋点
  • 使用低采样率(1-10%)

第二阶段

  • 全服务覆盖
  • 自定义 Tags
  • 集成告警

第三阶段

  • 迁移到 OpenTelemetry
  • 统一 Metrics + Logs + Tracing
  • 建立完整可观测性平台

下一节将详细介绍如何在 Kubernetes 中部署 Jaeger。