微服务链路追踪体系
一次用户请求在微服务里可能跨越 5-10 个服务、几十个调用。问题发生时,怎么定位"慢在哪一环、错在哪一环"?链路追踪就是答案。本文讲清数据模型、传播机制与主流方案选型。
为什么需要链路追踪
无追踪时的困境
用户请求 → 网关 → 订单服务 → 库存服务 → 支付服务 → 优惠券服务
问题:
1. 请求整体慢,不知道哪一环慢
2. 错误发生在下游,上游日志看不出关联
3. 无法把同一请求的日志串联起来
4. 无法量化各服务耗时占比追踪解决的问题
1. 调用拓扑可视化(请求流经哪些服务)
2. 每段调用耗时分解(定位瓶颈)
3. 全链路日志关联(一条 Trace 的所有日志)
4. 错误定位(哪一环抛错、传播路径)核心数据模型
Trace / Span / SpanContext
一次请求 = 一个 Trace(树状结构)
Trace 由若干 Span 组成
每个 Span 代表一次调用(服务间或服务内)Trace
└── Span A(网关→订单) root
├── Span B(订单→库存)
├── Span C(订单→支付)
│ └── Span D(支付→风控)
└── Span E(订单→优惠券)Span 结构
| 字段 | 含义 |
|---|---|
| traceId | 整条链路的唯一 ID(请求入口生成) |
| spanId | 当前 Span 的唯一 ID |
| parentSpanId | 父 Span ID(构成树结构) |
| operationName | 操作名(如 GET /order/1001) |
| startTime / endTime | 开始/结束时间(算耗时) |
| duration | 持续时间 |
| tags | 标签(HTTP 状态码、DB 语句等) |
| logs | 事件日志(错误、异常堆栈) |
| status | 状态(OK / ERROR) |
数据流
入口请求:生成 traceId = abc123
│
▼
服务A:spanId = 1,parentSpanId = null(根)
│ 调用服务B时传入 (traceId=abc123, spanId=2, parentSpanId=1)
▼
服务B:spanId = 3,parentSpanId = 2
│ 再传下去...
▼
各服务把 Span 上报到追踪系统 → 按 traceId 聚合 → 还原整棵树Context Propagation:上下文传播
传播的本质
跨进程调用时,需要把追踪上下文传递过去:
(traceId, spanId, sampled 标记) 随请求头传递HTTP 场景
http
GET /api/order/1001
X-B3-TraceId: abc123
X-B3-SpanId: 0000000000000002
X-B3-ParentSpanId: 0000000000000001
X-B3-Sampled: 1传播流程:
1. 入口生成 traceId
2. 调用下游前:创建子 Span,写入请求头
3. 下游解析请求头:取 traceId/spanId 续接
4. 调用结束:Span 打点、上报传播规范
B3 传播(Zipkin 传统):X-B3-TraceId / X-B3-SpanId ...
W3C tracecontext(标准):traceparent / tracestatetraceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
└─版本─┴────── trace-id ───────┴────── span-id ──────┴─标志传播的三要素
1. 注入(Inject):发送端写入上下文到请求头
2. 提取(Extract):接收端从请求头取出上下文
3. 续接(Continue):基于父 Span 创建子 Span主流方案对比
一览表
| 维度 | SkyWalking | Zipkin | Jaeger | OpenTelemetry |
|---|---|---|---|---|
| 定位 | 全栈 APM(追踪+指标+告警) | 纯追踪 | 追踪系统 | 可观测性标准 |
| 接入方式 | Agent 无侵入(字节码增强) | 探针/库埋点 | 库埋点 | SDK/Agent |
| 语言支持 | Java 优先,多语言 | 多语言 | 多语言 | 全语言 |
| 存储 | H2/ES/MySQL/OAP | ES/Cassandra/内存 | ES/Cassandra/内存 | 依赖后端 |
| 拓扑图 | 内置(自动生成) | 需插件 | 需插件 | 需后端 |
| 告警 | 内置 | 无 | 无 | 需整合 |
| 性能损耗 | 低(采样) | 低 | 低 | 低 |
| 生态 | 国内使用广 | 经典 | CNCF 毕业 | 标准趋同 |
选型建议
需要一体化 APM(追踪+监控+告警)、Java 系 → SkyWalking
仅需要分布式追踪、轻量 → Zipkin / Jaeger
想要标准化、厂商无关、多语言 → OpenTelemetry(标准)+ 任意后端SkyWalking 架构
核心组件
应用(Agent 探针)
│ gRPC 上报
▼
OAP Server(分析引擎)
│
├─ 接收 Segment/Span
├─ 聚合指标
├─ 告警判定
└─ 存储
│
▼
存储(ES / MySQL / H2)
│
▼
UI(Web 控制台:拓扑、追踪、指标、告警)特点
1. Agent 无侵入:Java Agent 字节码增强,业务代码零改动
2. 自动拓扑:服务间调用关系自动绘制
3. 采样控制:按百分比采样,控制成本
4. 告警内置:慢请求、错误率阈值告警Zipkin 架构
应用(brave 埋点)
│ HTTP/gRPC/Kafka 上报
▼
Zipkin Collector → Storage(ES/Cassandra)
▼
Zipkin UI(查询 Trace、火焰图)特点
1. 轻量纯粹:只做追踪
2. Brave SDK:Spring Cloud Sleuth 默认对接
3. 查询灵活:按 traceId/service/duration 检索Jaeger 架构
应用(jaeger-client)
│ UDP/gRPC
▼
jaeger-agent → jaeger-collector → Storage(ES)
▼
jaeger-query → jaeger-ui特点
1. CNCF 毕业项目,云原生友好
2. 支持 OpenTelemetry 协议
3. 采样策略灵活(固定/概率/速率限制)OpenTelemetry:可观测性标准
定位
不是独立产品,而是统一标准 + SDK:
Tracing + Metrics + Logs 三大信号的采集标准
生态:
应用 SDK/Agent → OTLP 协议 → 任意后端(SkyWalking/Jaeger/Prometheus)统一信号
Traces:链路追踪(span 数据)
Metrics:指标(计数器、直方图)
Logs:日志(关联 traceId)优势
1. 厂商无关:不被单一 APM 绑定
2. 标准统一:一套 SDK 多种后端
3. 三大信号关联:traceId 贯穿 metrics/logsSpring Cloud 中的接入方式
Sleuth(已并入 Micrometer Tracing)
java
// 依赖 spring-cloud-starter-sleuth(或 micrometer-tracing)
// 自动注入 traceId/spanId 到日志
@Slf4j
@RestController
public class OrderController {
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
// 日志自动带 traceId
log.info("查询订单: {}", id);
return orderService.getById(id);
}
}日志输出:
2026-08-03 10:00:01 [order-service,abc123,0000000000000002] 查询订单: 1001
└─traceId─┘ └──spanId──┘接入 SkyWalking
1. 下载 skywalking-agent
2. JVM 启动参数:
-javaagent:/opt/skywalking/skywalking-agent.jar
-Dskywalking.agent.service_name=order-service
3. 业务代码零改动,自动上报接入 Zipkin + RabbitMQ 上报
yaml
spring:
zipkin:
base-url: http://zipkin:9411
sender:
type: web # web / kafka / rabbit
sleuth:
sampler:
probability: 0.1 # 采样率 10%采样策略
为什么采样
全量追踪开销大(每请求多几个 Span 上报)
生产环境通常采样:全量(关键链路)/ 百分比(常规)| 策略 | 说明 |
|---|---|
| 全量采样 | 所有请求(低流量场景) |
| 概率采样 | 按百分比(默认常用) |
| 速率限制 | 每秒最多 N 条 |
| 尾部采样 | 只保留慢/异常请求 |
常见问题
- traceId 丢失? 检查异步线程是否传递上下文(ThreadLocal 不跨线程),需手动传播或使用装饰器。
- 采样率怎么定? 常规 10% 起步,核心链路 100%;按存储成本与问题定位需求权衡。
- 日志怎么关联 traceId? MDC 注入 traceId/spanId,日志框架自动输出,便于按 traceId 检索。
- SkyWalking 与 OTel 怎么选? 一体化 APM 选 SkyWalking;标准统一、多后端迁移选 OpenTelemetry。