Skip to content

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 / CRClient Sent / Client Received,客户端发起请求和收到响应
SR / SSServer 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)   ← 正常

如果有慢调用或异常,链路图中会以红色高亮显示,一眼就能定位问题服务。

六、生产最佳实践

  1. 日志中必须包含 TraceId:这是排查分布式问题的唯一手段
  2. 采样率配置:生产环境不需要 100% 采样,通常 10%-30% 即可,但错误请求必须 100% 采样
  3. TraceId 传透到异步线程:使用 @Async 时,需要配置 LazyTraceExecutor 包装线程池,确保 TraceId 不丢失
  4. 与 Feign 配合:Feign 自动透传 TraceId,但自定义 HTTP 调用需要手动处理