--- tags: [microservice, tracing, opentelemetry, context-propagation] create time: 2026-05-05 --- # 链路追踪 ## 概述 Tracing 回答的问题是:**请求在哪一步慢了 / 失败了?**——通过完整的调用链定位瓶颈和故障。 一次请求在多个服务中的完整调用路径: ```mermaid flowchart LR RID["Request
trace_id: abc-123"] subgraph SPANS["调用链 (Trace)"] direction TB S1["Span #1
API Gateway
5ms"] S2["Span #2
Order Service
70ms"] S3["Span #3
Payment RPC
160ms"] S4["Span #4
Inventory RPC
70ms"] S1 --> S2 --> S3 S2 --> S4 end RID --> S1 style S3 fill:#ff9999 note["Span #3 耗时最长 → Payment 是瓶颈"] S3 -.-> note ``` ## Trace/Span 核心概念 | 概念 | 说明 | |------|------| | **Trace** | 一次请求的完整调用链,全局唯一 `trace_id` | | **Span** | 调用链中的一个执行单元,有独立的 `span_id` | | **Parent-Child** | Span 树状结构,子 Span 继承父 Span 的 trace_id | | **Tags / Attributes** | 键值对标注(HTTP method、status code) | | **Logs** | Span 级别的时间戳事件(如 "DB query started") | | **Sampling** | 按比例采样,降低存储开销 | ### OpenTelemetry W3C Trace Context 标准 ```json // HTTP Header { "traceparent": "00-{trace_id}-{span_id}-01", "tracestate": "vendor=value" } ``` 格式解析:`version(2字节) - trace_id(32字节) - span_id(16字节) - flags(2字节)` ``` 示例: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 │ └─── trace_id ───┘ └─ span_id ─┘ └flags─┘ ``` ## 上下文传播详解 `trace_id` 必须从上游传递到下游。不同通信场景有不同的传播方式: ### HTTP 传播 ```go func Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 1. 提取入站 trace context ctx := propagator.Extract(r.Context(), headerReader{r.Header}) // 2. 创建新的 Span ctx, span := tracer.Start(ctx, "handleRequest") defer span.End() // 3. 将 trace context 注入出站请求 r = r.WithContext(ctx) propagator.Inject(ctx, headerWriter{r.Header}) next.ServeHTTP(w, r) }) } ``` ### MQ 消息头传播 ```go // 生产者 - 手动注入 trace context msg.Properties.Set("trace_id", extractTraceID(ctx)) msg.Properties.Set("span_id", extractSpanID(ctx)) // 消费者 - 恢复 trace context traceID := msg.Properties.Get("trace_id") span, _ := tracer.Start(traceContext, "consumeMessage", trace.WithAttributes(attribute.String("mq.message.id", traceID))) ``` ## 采样策略 ```mermaid flowchart TD AllReq["全部请求"] --> Sampler{"采样决策"} Sampler -->|"100%"| Core["核心链路全量采集"] Sampler -->|"5~10%"| Normal["普通路径随机采样"] Sampler -->|"100%"| Error["错误链路优先保留"] Sampler -->|"0%"| Health["健康检查/内部心跳"] Core --> Storage["Trace 存储"] Normal --> Storage Error --> Storage Health --> Drop["丢弃"] ``` | 环境 | 采样率 | 说明 | |------|--------|------| | 开发 / 测试 | 100% | 方便调试,无存储压力 | | 生产 - 核心链路 | 100% | 下单、支付等关键路径 | | 生产 - 普通路径 | 5~10% | 平衡成本和覆盖率 | | 生产 - 错误链路 | 100% | 出错时优先保留 | > [!tip] 基于错误的智能采样 > > 高级做法:**正常路径低采样,一旦检测到 HTTP 5xx 或 DB 超时,立即提升当前请求的采样率**。这样既省了存储,又能在出问题时有足够的数据回溯。 ## 主流方案对比 | 方案 | 存储后端 | 侵入程度 | 中文支持 | 推荐理由 | |------|---------|---------|---------|---------| | **OpenTelemetry** | 任意 Jaeger/Prometheus/Loki | SDK + Auto-instrumentation | 良好 | CNCF 行业标准,未来趋势 | | **Jaeger** | Cassandra / Elasticsearch | Agent / SDK | 社区支持 | Uber 开源,UI 优秀 | | **SkyWalking** | MySQL / ES | 零侵入 Agent | ⭐ 完善 | 国产首选,开箱即用 | ## 渐进式落地路线 > [!tip] 不要试图一步到位——先从 Tracing 开始,ROI 最高。 > > 1. **第一步**:接入 Tracing(OpenTelemetry),覆盖核心链路(下单、支付),rate=100% > 2. **第二步**:加 Metrics(Prometheus + Grafana),建立基础监控面板 > 3. **第三步**:集中 Logging(Loki / ELK),日志携带 trace_id 与 Tracing 关联 > 4. **第四步**:全量上 OpenTelemetry Collector,统一管理三件套 ## 性能影响评估 | 指标 | 预期影响 | |------|---------| | CPU 增加 | < 3%(异步批量上报) | | 内存增加 | ~10MB(Span buffer) | | 网络带宽 | 每条 Trace 约 2~5KB(取决于 Span 数量) | | 延迟增加 | < 0.1ms(SDK 内处理不阻塞业务) | > [!note] 关键实践 > - 使用 **BatchProcessor** 异步上报,不阻塞业务线程 > - Span 不要嵌套过深,一个 HTTP 调用或 DB 查询对应一个 Span > - 给 Span 设置丰富的 Attributes,方便查询和过滤 > - 错误 Span 标记 `isError=true`,便于快速定位问题链路 ## 关联笔记 - [[04-可观测性/01-Metrics监控]] — Tracing 与 Metrics 互补 - [[04-可观测性/02-日志系统]] — trace_id 串联日志和追踪 - [[04-可观测性/04-告警管理]] — 基于 Trace 数据的异常检测告警