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/ |
日志配置(同上) |