跳到正文
Kale的数字花园
目录 文章
/7 分钟/8 阅读/0 评论

Creeks 系统分布式链路追踪分析

项目使用 **Spring Cloud Sleuth** 做分布式链路追踪,**完全依赖自动配置,零手写代码**。所有 26 个微服务都在 中引入了 ,但**没有集成 Zipkin 做集中式可视化,也没有在业务代码中显式使用 Tracer/

分析日期:2026-05-09

一、一句话总结

项目使用 Spring Cloud Sleuth 做分布式链路追踪,完全依赖自动配置,零手写代码。所有 26 个微服务都在 pom.xml 中引入了 spring-cloud-starter-sleuth,但没有集成 Zipkin 做集中式可视化,也没有在业务代码中显式使用 Tracer/Span API。


二、整体架构

graph LR
    A[浏览器/客户端] --> B[creekson-api-web<br/>API 网关层]
    B --> C[creeks-allocation<br/>调拨服务]
    C --> D[creeks-inventory<br/>库存服务]
    D --> E[creeks-sales<br/>销售服务]
    C --> F[creeks-fulfillment<br/>履约服务]

    style B fill:#e1f5fe
    style C fill:#e1f5fe
    style D fill:#e1f5fe
    style E fill:#e1f5fe
    style F fill:#e1f5fe
同一个请求经过多个服务时,Sleuth 生成的 traceId 始终一致:

请求: Browser → API-Gateway → Allocation → Inventory
traceId: 463ac35c9f6413ad48485a3953bb6124  ← 全程不变

三、Sleuth 三板斧:核心机制

3.1 traceId/spanId 自动生成

sequenceDiagram
    participant Client
    participant ServiceA
    participant ServiceB
    participant ServiceC

    Client->>ServiceA: HTTP GET /api/order
    Note over ServiceA: traceId=xxx, spanId=AAA<br/>入口服务生成

    ServiceA->>ServiceB: Feign 调用 /api/inventory
    Note over ServiceA: 注入 X-B3-TraceId: xxx<br/>X-B3-SpanId: BBB<br/>X-B3-ParentSpanId: AAA

    Note over ServiceB: 提取 Header<br/>traceId=xxx, spanId=BBB

    ServiceB->>ServiceC: Feign 调用 /api/sales
    Note over ServiceB: 注入 X-B3-TraceId: xxx<br/>X-B3-SpanId: CCC<br/>X-B3-ParentSpanId: BBB

    Note over ServiceC: 提取 Header<br/>traceId=xxx, spanId=CCC

Sleuth 在请求进入时自动生成:

字段 说明 长度
traceId 全局唯一,标识一次完整的调用链路 16 字节(32 位十六进制)
spanId 单个服务内的操作标识 8 字节(16 位十六进制)
parentId 上级调用的 spanId 8 字节
span name 操作名称(通常是 HTTP 方法 + 路径) -

3.2 Feign 自动传递 trace 上下文

Sleuth 通过自动包装 FeignClient,在 HTTP Header 中注入 B3 协议 的 trace 信息:

X-B3-TraceId: 463ac35c9f6413ad48485a3953bb6124
X-B3-SpanId: a2fb4a8d1a4c9a46
X-B3-ParentSpanId: 9d7c5e1b2f3a8c7d
X-B3-Sampled: 1
b3: 463ac35c9f6413ad48485a3953bb6124-a2fb4a8d1a4c9a46-1

下游服务收到请求后,Sleuth 自动从 Header 中提取 traceId,创建新的 spanId,同一个 traceId 贯穿整个链路。

3.3 日志自动注入 traceId

引入 Sleuth 后,Spring Boot 的日志格式自动变为:

[creeks-allocation,463ac35c9f6413ad,48485a3953bb6124] 2026-05-09 10:30:00.123 INFO 12345 --- [http-nio-8080-exec-1] c.c.t.c.a.b.AllocationOrderBusinessImpl : 创建调拨单成功
 ^服务名              ^traceId           ^spanId

格式: [applicationName,traceId,spanId]

项目的 logback 配置继承了 Spring Boot defaults,这就是 Sleuth 自动注入的入口:

<!-- logback-spring.xml(所有服务都是这个结构) -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

<property name="LOG_FILE" value="logs/creeks-allocation.log"/>
<property name="LOG_FILE_MAX_SIZE" value="100MB"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml"/>

<!-- 异常日志统一收集到 error-collect 服务 -->
<appender name="ERROR_COLLECT" class="com.codo.tech.error.collect.client.LogCollectAppender">
    <token>dTrPeZSAjrbG3WbhLi8EDzmbIiNLedJDq2EiagXZAMBoeC6zaROSmeaA7smgCoGP</token>

</appender>

四、覆盖范围:26 个服务全部引入

服务类别 服务名 依赖
核心业务 creeks-allocation(调拨) spring-cloud-starter-sleuth
核心业务 creeks-inventory(库存) spring-cloud-starter-sleuth
核心业务 creeks-receiving(收货) spring-cloud-starter-sleuth
核心业务 creeks-fulfillment(履约) spring-cloud-starter-sleuth
核心业务 creeks-purchasing(采购) spring-cloud-starter-sleuth
核心业务 creeks-logistics(物流) spring-cloud-starter-sleuth
核心业务 creeks-finance(财务) spring-cloud-starter-sleuth
核心业务 creeks-statistics(统计) spring-cloud-starter-sleuth
核心业务 creeks-warehouse(仓储) spring-cloud-starter-sleuth
核心业务 creeks-product-query(商品查询) spring-cloud-starter-sleuth
数据同步 creeks-cdc(CDC 数据同步) spring-cloud-starter-sleuth
数据同步 creeks-order-center(订单中心) spring-cloud-starter-sleuth
数据同步 creekshub-sync-amazon(Amazon 同步) spring-cloud-starter-sleuth
数据同步 creekshub-account(账号服务) spring-cloud-starter-sleuth
平台同步 esync-amazon(Amazon) spring-cloud-starter-sleuth
平台同步 esync-ebay(eBay) spring-cloud-starter-sleuth
平台同步 esync-aliexpress(速卖通) spring-cloud-starter-sleuth
平台同步 esync-temu(Temu) spring-cloud-starter-sleuth
平台同步 esync-shein(Shein) spring-cloud-starter-sleuth
平台同步 esync-walmart(Walmart) spring-cloud-starter-sleuth
支撑服务 creekson-api-web(API 网关) spring-cloud-starter-sleuth
支撑服务 creekson-mall(商城) spring-cloud-starter-sleuth
支撑服务 creeks-sales(销售) spring-cloud-starter-sleuth
支撑服务 creeks-dingtalk(钉钉) spring-cloud-starter-sleuth
支撑服务 creeks-monitor(监控) spring-cloud-starter-sleuth
支撑服务 creeks-service-template(模板) spring-cloud-starter-sleuth

五、Sleuth 自动支持的场景 vs 可能遗漏的场景

5.1 自动支持的场景

场景 是否自动传递 实现机制
HTTP 请求 自动 TraceFilter 自动处理入口和出口
Feign 远程调用 自动 TraceFeignClient 自动注入 B3 Header
RestTemplate 自动 TraceRestTemplateInterceptor
@Async 异步方法 自动 LazyTraceExecutor / TraceableExecutorService
Kafka 消息 自动 TracingProducerInterceptor + TracingConsumerInterceptor
日志输出 自动 MDC 自动注入 traceId/spanId
Spring Messaging (RabbitMQ) 自动 Sleuth 的 Messaging 拦截器

5.2 可能遗漏的场景

场景 问题 风险等级
Kafka 消费者内部再发新 Topic GoodsSyncConsumer 收到消息后转发到 ebay_item/other_item,Sleuth 会为转发创建新 trace,跟原 trace 断开 高
CompletableFuture 异步任务 CompletableFuture.supplyAsync(executor) 如果用的不是 Sleuth 包装的 Executor,traceId 不会传递到子线程 高
定时任务入口 @Scheduled 或 Quartz 触发的定时任务,Sleuth 不一定能正确注入 traceId 中
creeks-maxwell Java 之外的组件(Maxwell 是 Scala),不参与 Sleuth 体系 低(天然不支持)

Kafka 转发链路断开的例子

// GoodsSyncConsumer.java
@KafkaListener(topics = "cdc_api_mall_goods", ...)
public void processGoodsSyncToEs(List<String> data, Acknowledgment ack) {
    // Sleuth 自动关联:traceId = AAA
    for (CdcData cdcData : cdcDataList) {
        if (isEbay(cdcData)) {
            kafkaHelper.send(ebayItem, ...);
            // ← 这里发送的消息会生成新的 traceId = BBB
            // 跟原始的 traceId = AAA 断开了!
        }
    }
}

原因: Kafka 消费者自动关联的 traceId 是 AAA,但生产者发送新消息时,Sleuth 会创建新的 span(而不是继续使用 AAA 的子 span)。虽然理论上 Sleuth 的 Kafka 生产者拦截器应该携带当前 traceId,但如果业务代码在消费者内部手动创建新的发送上下文,可能会出现断链。


六、缺失的部分

6.1 没有 Zipkin 集中式可视化

虽然引入了 Sleuth 生成 traceId,但没有引入 Zipkin sender:

<!-- 没有看到以下依赖 -->
<dependency>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>

</dependency>

<!-- 没有看到以下配置 -->
spring.zipkin.base-url=http://zipkin-server:9411

后果: traceId 只在日志里打印出来,没法在 Web UI 上查看:

  • 调用链路拓扑图
  • 每个服务节点的耗时分析
  • 调用关系依赖图
  • 慢调用定位

排查问题时只能 grep 日志文件找同一个 traceId,效率低。

6.2 没有采样率配置

没有看到 spring.sleuth.sampler.probability 配置,默认 100% 采样。

对于高并发场景(如订单采集、CDC 数据同步),全量采样会:

  • 产生大量 trace 数据
  • 增加日志存储压力
  • 影响性能(每个请求都要生成 traceId/spanId)

建议配置:

spring:
  sleuth:
    sampler:
      probability: 0.1  # 10% 采样率

6.3 没有业务代码显式使用 Tracer API

搜遍所有业务代码,没有任何一处显式使用 Tracer、Span** 等 Sleuth API**:

// 没有这样的代码
@Autowired private Tracer tracer;
Span span = tracer.nextSpan().name("my-operation").start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
    // 业务逻辑
} finally {
    span.finish();
}

完全依赖自动配置,粒度只到 HTTP 请求级别,无法追踪方法级别的耗时。


七、实际使用效果总结

能做到的

能力 说明
traceId 全程一致 同一个 HTTP 请求经过多个微服务,日志中 traceId 一致
Feign 自动传递 不需要手写代码,Feign 调用自动注入 B3 Header
日志包含 traceId 每条日志都带 [服务名,traceId,spanId],可以 grep traceId 串联
Kafka 自动携带 Kafka 消息自动携带 trace 信息
@Async 自动传递 异步方法调用自动传递 traceId
异常统一收集 各服务通过 ERROR_COLLECT appender 统一上报到 error-collect 服务

做不到的

缺失能力 影响
无 Zipkin 集中式可视化 没有调用链路拓扑图,只能 grep 日志文件
无业务代码显式 Span 埋点 无法追踪单个方法内部的耗时瓶颈
无采样率配置 100% 采样,高并发场景压力大
Kafka 转发链路可能断开 消费者内部再发新 Topic 时 traceId 可能不一致
CompletableFuture trace 可能丢失 自定义线程池没被 Sleuth 包装时,子线程丢失 traceId

八、排查问题的实际操作方式

# 已知一个请求返回了报错,但没有 traceId 信息的情况下:
# 1. 通过时间戳 + 关键词找到入口服务的日志
grep "创建调拨单失败" logs/creeks-allocation.log

# 2. 从日志中提取 traceId(格式:[creeks-allocation,xxxxx,yyyyy])
# 假设 traceId 为 463ac35c9f6413ad48485a3953bb6124

# 3. 在各个服务日志中搜索同一个 traceId
grep "463ac35c9f6413ad" logs/creeks-inventory.log
grep "463ac35c9f6413ad" logs/creeks-fulfillment.log
grep "463ac35c9f6413ad" logs/creeks-sales.log

# 4. 手动拼接出完整的调用链路

痛点:

  • 日志分散在各服务器本地文件
  • 需要逐个服务 grep
  • 没有时间线视图,只能人工还原顺序
  • Kafka 异步消费链路无法通过 traceId 关联

九、改进建议

9.1 接入 Zipkin 集中式可视化

<!-- 在父 pom.xml 中添加 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-sleuth-zipkin</artifactId>

</dependency>
# application.yml
spring:
  zipkin:
    base-url: http://zipkin-server:9411
  sleuth:
    sampler:
      probability: 0.1  # 10% 采样率

效果:

  • Web UI 上直接查看调用链路拓扑图
  • 每个服务节点的耗时一目了然
  • 自动发现慢调用和异常调用

9.2 关键业务显式埋点

对耗时较长或链路较长的核心方法,显式创建 Span:

@Autowired private Tracer tracer;

public void calculatePackageStockQuantity(String orderNo) {
    Span span = tracer.nextSpan()
        .name("allocation.calculatePackageStock")
        .tag("orderNo", orderNo)
        .start();
    try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
        // 业务逻辑
    } catch (Exception e) {
        span.error(e);
        throw e;
    } finally {
        span.finish();
    }
}

9.3 CompletableFuture 使用 Sleuth 包装的 Executor

// 改造前
CompletableFuture.supplyAsync(() -> {...}, completableFutureTaskExecutor);

// 改造后
@Autowired private BeanFactory beanFactory;
// 获取 Sleuth 包装的 Executor
Executor executor = new LazyTraceExecutor(beanFactory, completableFutureTaskExecutor);
CompletableFuture.supplyAsync(() -> {...}, executor);

9.4 统一采样率配置

spring:
  sleuth:
    sampler:
      probability: 0.1   # 默认 10% 采样
    # 对特定路径可以调整采样
    web:
      skip-pattern: /actuator/.*|/health

十、关键文件索引

文件 路径 作用
pom.xml (allocation) creeks-allocation/.../pom.xml:184 引入 spring-cloud-starter-sleuth
pom.xml (cdc) creeks-cdc/.../pom.xml:161 引入 spring-cloud-starter-sleuth
pom.xml (sales) creeks-sales/.../pom.xml:175 引入 spring-cloud-starter-sleuth
logback-spring.xml creeks-allocation/.../resources/ 日志配置(继承 Spring Boot defaults)
logback-spring.xml creeks-cdc/.../resources/ 日志配置(同上)
相关阅读

3 分钟

Sleuth MDC分布式链路追踪

在分布式系统下,存在多链路的微服务调用,在排查问题时需要去追踪各个服务的调用链过程是比较麻烦的,所以需要引入一个分布式链路追踪的机制 https://mp.weixin.qq.com/s/cyGEDNYMDAKwQgNEFQV4cw 其中调

面试#分布式链路追踪6 阅读 · 0 评论

评论需要登录。生产环境使用 GitHub 登录,本地可前往 登录页 使用开发登录。

还没有评论,来抢沙发。