Sleuth 详解:链路追踪
Spring Cloud Sleuth(现整合为 Micrometer Tracing)是 Spring Cloud 生态中的分布式链路追踪组件。它自动为每个请求生成 TraceId 和 SpanId,并在服务间调用时透传,最终将链路数据上报到 Zipkin / SkyWalking 等可视化平台。
一、核心概念
一次完整的用户请求链路:
TraceId: a1b2c3d4-xxxx ──── 贯穿整个请求链路的唯一标识
┌──────────────────────────────────────────────────────────────────┐
│ Span1: Gateway │
│ SpanId: s1, ParentSpanId: null │
│ Duration: 2ms │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Span2: order-service │ │
│ │ SpanId: s2, ParentSpanId: s1 │ │
│ │ Duration: 50ms │ │
│ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │
│ │ │ Span3: inventory-svc │ │ Span4: payment-svc │ │ │
│ │ │ SpanId: s3 │ │ SpanId: s4 │ │ │
│ │ │ ParentSpanId: s2 │ │ ParentSpanId: s2 │ │ │
│ │ │ Duration: 30ms │ │ Duration: 15ms │ │ │
│ │ └──────────────────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘| 概念 | 说明 |
|---|---|
| TraceId | 一次完整请求链路的全局唯一标识,64 位 |
| SpanId | 链路中每个操作单元的唯一标识,64 位 |
| ParentSpanId | 父 Span 的 ID,用于构建 Span 之间的父子关系 |
| CS / CR | Client Sent / Client Received,客户端发起请求和收到响应 |
| SR / SS | Server Received / Server Sent,服务端收到请求和返回响应 |
二、TraceId 透传机制
服务 A (order-service) 服务 B (inventory-service)
─────────────── ────────────────
1. 收到请求,Sleuth 生成 TraceId
TraceId: abc123
SpanId: s1
2. 调用 inventory-service
Feign RequestInterceptor 拦截
↓
请求头中添加:
X-B3-TraceId: abc123 ← TraceId 透传
X-B3-SpanId: s2
X-B3-ParentSpanId: s1
───────────────────────────→ 3. 收到请求,解析请求头
TraceId: abc123
SpanId: s3
ParentSpanId: s2
4. 处理业务逻辑
5. 返回响应
←───────────────────────────关键点: Sleuth 通过 X-B3-* 系列请求头透传链路信息,Feign 的 RequestInterceptor 和 Gateway 的 GlobalFilter 自动完成透传,无需手动编码。
三、日志输出配置
在 logback-spring.xml 中配置,让每条日志都带上 TraceId:
xml
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-},%X{spanId:-}]
%-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>输出效果:
2024-01-15 10:30:45.123 [http-nio-8081-exec-1] [abc123,s1] INFO OrderService - 开始下单
2024-01-15 10:30:45.456 [http-nio-8082-exec-1] [abc123,s3] INFO InventoryService - 扣减库存
2024-01-15 10:30:45.789 [http-nio-8083-exec-1] [abc123,s4] INFO PaymentService - 创建支付单排查问题时,只需要一个 TraceId,就能在 ELK 中搜索出整个调用链的所有日志。
四、集成 SkyWalking
SkyWalking 是国产 APM 系统,更适合国内场景:
xml
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>9.1.0</version>
</dependency>java
@RestController
public class OrderController {
@GetMapping("/order/{id}")
@Trace // SkyWalking 注解
@Tag(key = "orderId", value = "arg[0]") // 将参数作为标签
public Order getOrder(@PathVariable Long id) {
return orderService.getById(id);
}
}五、调用链可视化
在 SkyWalking 控制台中,输入 TraceId 可以看到完整的调用链拓扑:
Gateway (2ms)
└── order-service (50ms)
├── inventory-service (30ms) ← 正常
└── payment-service (15ms) ← 正常
如果有慢调用或异常,链路图中会以红色高亮显示,一眼就能定位问题服务。六、生产最佳实践
- 日志中必须包含 TraceId:这是排查分布式问题的唯一手段
- 采样率配置:生产环境不需要 100% 采样,通常 10%-30% 即可,但错误请求必须 100% 采样
- TraceId 传透到异步线程:使用
@Async时,需要配置LazyTraceExecutor包装线程池,确保 TraceId 不丢失 - 与 Feign 配合:Feign 自动透传 TraceId,但自定义 HTTP 调用需要手动处理