Sleuth + Zipkin 分布式链路追踪
导学
双十一结束后,大翔在复盘会上问了一个问题:"27 号凌晨 1 点,用户小王的订单支付超时了。问题是出在订单服务本身、支付服务响应慢、还是网络抖动?谁能告诉我这笔订单到底经过了哪些服务,各自耗时多少?"
所有人沉默了。小崔翻了半天日志:"订单服务的日志在文件 A,支付服务的在文件 B,库存的又在另一个文件夹...而且要按 Trace ID 手动关联。"
白歌说:"这就是分布式追踪要解决的问题。Spring Cloud 提供了 Micrometer Tracing + Zipkin 方案。"
定位与问题场景
微服务架构中排查问题的三大痛点:
核心原理
Trace 和 Span
- Trace(追踪):一次完整的请求链路,由唯一的 Trace ID 标识
- Span(跨度):链路中的一个基本工作单元,记录操作名称、开始/结束时间、标签和事件
- 上下文传播:Trace ID 和 Span ID 通过 HTTP Header(
traceparent)在服务间传递
Sleuth 演进(Spring Boot 3.x 重要变更)
关键变更:Spring Cloud Sleuth 已进入维护模式。Spring Boot 3.x + Spring Cloud 2022.0.x 起,使用 Micrometer Tracing 作为统一的可观测性门面。
| 版本 | 追踪方案 | 依赖 |
|---|---|---|
| Spring Boot 2.x | Spring Cloud Sleuth | spring-cloud-starter-sleuth |
| Spring Boot 3.x | Micrometer Tracing | micrometer-tracing-bridge-brave + zipkin-reporter-brave |
完整示例:飞翔科技全链路追踪
场景描述
订单服务 → 库存服务 → 支付服务的调用链路,使用 Micrometer Tracing + Zipkin 实现全链路追踪和可视化。
操作前后对比:
| 维度 | 引入 Tracing 前 | 引入 Tracing 后 |
|---|---|---|
| 跨服务日志关联 | 手动通过时间戳猜测 | Trace ID 自动串联所有服务日志 |
| 调用链拓扑 | 凭记忆画架构图 | Zipkin UI 自动生成拓扑图 |
| 耗时瓶颈 | 各服务日志分开看 | Span 树直观展示每个环节耗时 |
步骤一:依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
步骤二:配置
spring:
application:
name: feixiang-order-service
management:
tracing:
sampling:
probability: 1.0 # 100% 采样(开发环境;生产环境建议 0.1)
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
步骤三:业务代码(无需额外注解)
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{orderId}")
public Order getOrder(@PathVariable String orderId) {
// Trace ID 自动注入 MDC,日志自动携带
log.info("查询订单:orderId={}", orderId);
// 调用库存服务(Trace 自动传播)
StockDTO stock = restTemplate.getForObject(
"http://FEIXIANG-INVENTORY-SERVICE/inventory/" + orderId,
StockDTO.class
);
// 调用支付服务(Trace 自动传播)
PaymentDTO payment = restTemplate.getForObject(
"http://FEIXIANG-PAYMENT-SERVICE/payment/" + orderId,
PaymentDTO.class
);
return Order.builder()
.orderId(orderId)
.stock(stock)
.payment(payment)
.build();
}
}
步骤四:自定义 Span
@RestController
public class OrderController {
@Autowired
private ObservationRegistry observationRegistry;
@GetMapping("/order/create")
public Order createOrder(@RequestBody OrderRequest request) {
// 创建自定义 Span 记录业务操作
return Observation.createNotStarted("order.create-business-logic", observationRegistry)
.lowCardinalityKeyValue("order.type", request.getType())
.observe(() -> {
// 业务逻辑
Order order = orderService.create(request);
log.info("订单创建成功:orderId={}", order.getId());
return order;
});
}
}
步骤五:启动 Zipkin Server
docker run -d --name zipkin \
-p 9411:9411 \
openzipkin/zipkin:3
访问 http://localhost:9411,输入 Trace ID 即可查看完整的调用链拓扑图和耗时分布。
步骤六:Logback 配置 Trace ID
<!-- logback-spring.xml -->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-}] [%X{spanId:-}] %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
日志输出示例(三个服务的日志中 Trace ID 一致):
# 订单服务
14:32:01.123 [http-nio-8080] INFO [abc123] [span-1] OrderController - 查询订单:orderId=1001
# 库存服务
14:32:01.245 [http-nio-8081] INFO [abc123] [span-2] InventoryController - 扣减库存
# 支付服务
14:32:01.567 [http-nio-8082] INFO [abc123] [span-3] PaymentController - 发起支付
监控体系扩展
Prometheus + Grafana
management:
endpoints:
web:
exposure:
include: health,info,prometheus
metrics:
tags:
application: ${spring.application.name}
# prometheus.yml
scrape_configs:
- job_name: 'spring-boot-services'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8080', 'localhost:8081', 'localhost:8082']
易错场景
1. Spring Boot 3.x 引入 spring-cloud-starter-sleuth 导致启动失败
Spring Boot 3.x 中 Sleuth 已被 Micrometer Tracing 替代。如果引入 spring-cloud-starter-sleuth,会产生冲突。
2. MDC 中的 Trace ID 键名变化
| 版本 | Trace ID MDC 键名 |
|---|---|
| Sleuth | traceId |
| Micrometer Tracing (Brave) | traceId |
| Micrometer Tracing (OTel) | trace_id |
确保 logback 配置的 %X{...} 键名与使用的 Tracing Bridge 一致。
3. 异步线程 Trace ID 丢失
// ❌ 错误:异步线程中 Trace ID 丢失
@Async
public void sendNotification(String orderId) {
log.info("发送通知"); // MDC 中没有 Trace ID
}
// ✅ 正确:使用 Context Propagation
@Async
public void sendNotification(String orderId) {
try (var scope = observationRegistry.getCurrentObservation().openScope()) {
log.info("发送通知"); // MDC 中有 Trace ID
}
}
4. 生产环境 100% 采样导致性能下降
# ❌ 生产环境
management.tracing.sampling.probability: 1.0
# ✅ 生产环境:10% 采样
management.tracing.sampling.probability: 0.1
面试考点
Trace 和 Span 的区别?
Trace 是一次完整请求链路的全局标识(Trace ID),由一系列 Span 组成。Span 是链路中单个操作单元,记录操作名称、父子关系、开始/结束时间戳、标签和事件。一个 Trace 是一棵树,根 Span 是最初的请求入口,子 Span 是下游调用,所有 Span 共享同一个 Trace ID。
Sleuth 为什么被 Micrometer Tracing 取代?
Micrometer 是 Spring 生态的统一可观测性门面(Metrics + Tracing),Sleuth 是 Spring Cloud 的独立追踪方案。Micrometer Tracing 将追踪融入 Micrometer 体系,与 Micrometer Metrics 共享
ObservationAPI,实现 Metrics + Traces 的统一。同时支持 Brave 和 OpenTelemetry 两种实现,给用户更多选择。
Zipkin、Jaeger、SkyWalking 的对比?
工具 定位 特点 Zipkin 轻量追踪 部署简单,资源占用小,仅追踪 Jaeger 分布式追踪 CNCF 项目,云原生,Uber 开源 SkyWalking APM 全栈 追踪+指标+日志+拓扑+告警,探针无侵入 中小系统或仅需追踪 → Zipkin;全面可观测+国内生态 → SkyWalking;云原生/K8s → Jaeger。
小结
分布式追踪让微服务的"黑盒"调用变得透明。通过 Trace ID 串联分散的日志,通过 Zipkin 可视化调用链拓扑,排查问题时不再是盲人摸象。Micrometer Tracing 作为 Sleuth 的继任者,将追踪融入了 Spring 生态的一体化可观测体系。至此,Spring Cloud 的核心组件全部讲解完毕,最后一章我们进行最佳实践总结和面试考点梳理。