Appearance
Sleuth + Zipkin 极简教程
一、基础理论
1. 定位
- 分布式链路追踪:解决微服务架构下一次请求跨多个服务的调用链可视化与性能分析。
- 快速定位故障:通过 TraceId 串联全链路,一眼看出哪一环节慢、哪一环节报错。
- Spring Cloud 官方方案:Sleuth 负责自动埋点 + 上下文传播;Zipkin 负责数据收集与 UI 展示。
2. 核心概念
| 概念 | 说明 |
|---|---|
| Trace | 一次完整请求的调用链,由唯一的 TraceId 标识。 |
| Span | 调用链中的基本工作单元(如一次 HTTP 请求、一次 DB 查询、一次 Feign 调用)。每个 Span 有唯一的 SpanId。 |
| Parent / Child Span | Span 之间构成父子关系,如 A 服务调用 B 服务,A 的 Span 是 Parent,B 的 Span 是 Child。 |
| Annotation | 标注 Span 上的关键事件:CS(Client Send)、SR(Server Receive)、SS(Server Send)、CR(Client Receive)。 |
| Baggage | 随 Trace 上下文跨服务透传的自定义键值对(如用户 ID、租户 ID),类似 HTTP Header 但自动传播。 |
| Sampling | 采样策略,控制上报比例(如 10%),避免高并发下追踪系统被打爆。 |
3. 核心原理
- 自动埋点:Sleuth 通过 Spring AOP / 拦截器 / Filter 自动拦截常见的调用组件:
- HTTP:
Servlet/WebFlux/Gateway - RPC:OpenFeign / RestTemplate / WebClient
- 消息:RabbitMQ / Kafka
- 数据库:JdbcTemplate / R2DBC(部分支持)
- HTTP:
- 上下文注入:请求发起时,Sleuth 生成 TraceId + SpanId,通过 HTTP Header 注入下游(如
X-B3-TraceId、X-B3-SpanId)。 - 日志集成:TraceId / SpanId 自动写入 MDC,日志输出中可打印
[service-name,trace-id,span-id,exportable]。 - 数据上报:Span 完成后通过 HTTP / Kafka / RabbitMQ 异步上报到 Zipkin Server。
- Zipkin 展示:按 TraceId 聚合,生成调用拓扑图和耗时瀑布流。
TraceId 透传拆解:
- 请求进入 Gateway,Sleuth Filter 生成
TraceId=T1, SpanId=S1。- Gateway 通过 Feign/RestTemplate 调用 order-service,请求头自动带上
X-B3-TraceId=T1, X-B3-ParentSpanId=S1。- order-service 收到后,以
T1为 TraceId,生成新的SpanId=S2,记录自己的处理耗时。- order-service 再调用 storage-service,继续透传
T1,生成SpanId=S3。- Zipkin 收到各服务的 Span 后,按
TraceId=T1聚合,展示完整的调用树。
4. 采样策略
| 策略 | 说明 |
|---|---|
| Probability | 按概率采样(如 0.1 表示 10% 请求被追踪),默认策略。 |
| RateLimiter | 限制每秒上报的 Trace 数量(如 10 表示每秒最多 10 条)。 |
| Always | 全部采样,开发测试用,生产慎用。 |
| Never | 全部不采样,相当于关闭链路追踪。 |
5. 与 SkyWalking / Jaeger 对比
| 维度 | Spring Cloud Sleuth + Zipkin | Apache SkyWalking | Jaeger |
|---|---|---|---|
| 侵入性 | 低(加依赖即可自动埋点) | 无侵入(Java Agent) | 低 |
| 编程语言 | Java 为主 | 多语言(Java/Go/Node.js 等) | 多语言 |
| 功能 | 基础链路 + 耗时分析 | 更丰富(性能剖析、告警、拓扑、日志关联) | 功能中等 |
| 存储 | MySQL / Elasticsearch / Cassandra | Elasticsearch / H2 / MySQL / TiDB / BanyanDB | Elasticsearch / Cassandra |
| 部署复杂度 | 简单(Jar 或 Docker 一键启动) | 需部署 OAP + UI + 存储 | 中等 |
| Spring 生态 | 原生深度集成 | 需 Agent 附加 | 需 OpenTelemetry |
| 适用场景 | Spring Cloud 微服务入门 | 大型异构系统、全链路监控 | 云原生多语言栈 |
选型建议:纯 Spring Cloud 生态用 Sleuth + Zipkin 开箱即用;异构多语言或需要深度监控选 SkyWalking。
二、工作实践(复制即用)
1. Zipkin 服务端部署
Docker 一键启动(内存模式,开发测试)
bash
docker run --name zipkin \
-p 9411:9411 \
-d openzipkin/zipkin:latest
# UI 访问:http://localhost:9411生产推荐:Elasticsearch 存储
bash
docker run --name zipkin \
-p 9411:9411 \
-e STORAGE_TYPE=elasticsearch \
-e ES_HOSTS=http://localhost:9200 \
-d openzipkin/zipkin:latest2. 基础依赖
每个需要追踪的服务都引入
xml
<!-- Sleuth + Zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>⚠️ Spring Cloud 2020+ 后,
spring-cloud-starter-zipkin已废弃,统一用spring-cloud-sleuth-zipkin。
3. 基础配置(application.yml)
yaml
spring:
application:
name: order-service
zipkin:
base-url: http://localhost:9411 # Zipkin Server 地址
sender:
type: web # web/kafka/rabbit;默认 web(HTTP 上报)
sleuth:
sampler:
probability: 1.0 # 采样率:1.0=100%(开发用),生产建议 0.1~0.3
propagation:
type: w3c,b3 # 传播协议:w3c(TraceContext)+ b3(Zipkin 原生)兼容4. 日志中打印 TraceId(logback-spring.xml)
Sleuth 自动将追踪信息注入 MDC,只需在日志格式中引用:
xml
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{36} - %msg%n</pattern>
</encoder>
</appender>关键变量:
%X{traceId:-}和%X{spanId:-};若不存在则显示空。
日志输出示例
2026-05-10 12:30:15.123 [http-nio-8081-exec-1] INFO [a1b2c3d4e5f6g7h8,i9j0k1l2m3n4o5p6] c.e.o.controller.OrderController - 创建订单:userId=10015. 网关层透传 TraceId(Spring Cloud Gateway)
Gateway 已自动集成 Sleuth,TraceId 会自动生成并向下游透传。如需在响应头中返回 TraceId 方便前端排查:
java
@Component
@Slf4j
public class TraceIdResponseFilter implements GlobalFilter, Ordered {
@Autowired
private Tracer tracer;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
Span span = tracer.currentSpan();
if (span != null) {
String traceId = span.context().traceId();
exchange.getResponse().getHeaders().add("X-Trace-Id", traceId);
}
}));
}
@Override
public int getOrder() {
// 在响应返回阶段执行,放最后
return Ordered.LOWEST_PRECEDENCE;
}
}6. 自定义 Span 与 Tag(手动埋点)
自动埋点覆盖 HTTP/RPC/DB 等通用场景;业务关键逻辑可手动创建 Span。
java
@Service
@Slf4j
public class OrderService {
@Autowired
private Tracer tracer;
public void complexBizLogic() {
// 创建自定义 Span,名称为 "complex-calculation"
Span newSpan = tracer.nextSpan().name("complex-calculation");
try (Tracer.SpanInScope ws = tracer.withSpanInScope(newSpan.start())) {
newSpan.tag("order.biz.type", "calculate-price");
newSpan.annotate("calculation-start");
// 业务逻辑...
calculatePrice();
newSpan.annotate("calculation-end");
} finally {
newSpan.end(); // 必须 end,否则不会上报
}
}
}7. Baggage 跨服务透传自定义字段
Baggage 会随 Trace 上下文自动注入到所有下游请求头中。
配置
yaml
spring:
sleuth:
baggage:
remote-fields:
- tenant-id # 允许从上游接收的 Baggage 字段
- user-id
local-fields:
- tenant-id # 本地可设置的 Baggage 字段
- user-id
tag-fields:
- tenant-id # 将 Baggage 值同时写入 Span Tag,Zipkin 中可检索代码设置
java
@Service
public class SomeService {
@Autowired
private Tracer tracer;
public void setContext() {
// 设置 Baggage
tracer.currentSpan().context().updateBaggage("tenant-id", "tenant_001");
tracer.currentSpan().context().updateBaggage("user-id", "10086");
}
}下游服务直接读取
java
@Autowired
private Tracer tracer;
public void getContext() {
String tenantId = tracer.currentSpan().context().getBaggage("tenant-id").get();
String userId = tracer.currentSpan().context().getBaggage("user-id").get();
}8. Kafka 上报(高并发生产环境)
HTTP 上报在超高并发下可能成为瓶颈,建议改用 Kafka。
增加依赖
xml
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>配置
yaml
spring:
zipkin:
sender:
type: kafka
kafka:
bootstrap-servers: localhost:9092Zipkin Server 同时配置消费 Kafka:
bash
docker run --name zipkin \
-p 9411:9411 \
-e KAFKA_BOOTSTRAP_SERVERS=localhost:9092 \
-d openzipkin/zipkin:latest三、核心速查表
| 概念/配置 | 说明 |
|---|---|
Trace | 一次完整调用链,唯一 TraceId |
Span | 调用链中的单个工作单元,唯一 SpanId |
spring-cloud-starter-sleuth | Sleuth 自动埋点依赖 |
spring-cloud-sleuth-zipkin | Zipkin 数据上报依赖 |
spring.zipkin.base-url | Zipkin Server 地址 |
spring.sleuth.sampler.probability | 采样率:1.0=100%,0.1=10% |
X-B3-TraceId | Sleuth 默认透传的 HTTP Header |
Tracer | Sleuth 核心 API,用于手动创建 Span |
Baggage | 跨服务透传的自定义键值对 |
MDC | 日志上下文,%X{traceId} 打印到日志 |
spring.sleuth.baggage.remote-fields | 允许从上游接收的 Baggage 字段列表 |
spring.sleuth.baggage.tag-fields | 同时写入 Zipkin Tag 的 Baggage 字段 |
zipkin.sender.type | 上报方式:web / kafka / rabbit |
四、排错速查
| 现象 | 解决 |
|---|---|
| Zipkin UI 看不到 Trace | 1. 检查 spring.zipkin.base-url 是否正确2. 检查采样率是否 > 0( probability 默认可能是 0)3. 检查服务间调用是否通过 Sleuth 支持的组件(Feign/RestTemplate/WebClient),直连 URL 不会传播 TraceId |
| 日志中没有 TraceId | 1. 确认引入了 spring-cloud-starter-sleuth2. 确认日志配置包含 %X{traceId} 或 %X{spanId}3. 确认使用了支持 MDC 的日志框架(logback/log4j2) |
| TraceId 中断(下游服务 TraceId 变了) | 1. 检查下游服务是否也引入了 Sleuth 2. 检查是否使用了自定义 HTTP 客户端(如 OkHttp 未配置拦截器) 3. 检查 Gateway 是否有自定义 Filter 误删了 X-B3-* Header |
| Baggage 透传失败 | 1. 检查 spring.sleuth.baggage.remote-fields 是否包含该字段名2. Baggage 设置后必须在同一个 Trace 内,新 Trace 不会继承 |
| Zipkin Server 内存溢出 | 内存模式只适合开发;生产务必切换为 Elasticsearch / Cassandra / MySQL |
| HTTP 上报延迟高/丢数据 | 高并发下改用 Kafka / RabbitMQ 异步上报 |
| Sleuth 与 Spring Boot 版本冲突 | Spring Cloud 与 Spring Boot 版本必须匹配,参考官方版本说明 |
| 手动创建的 Span 未上报 | 检查是否调用了 span.end(),未 end 的 Span 不会上报 |