Skip to content

Sleuth + Zipkin 极简教程


一、基础理论

1. 定位

  • 分布式链路追踪:解决微服务架构下一次请求跨多个服务的调用链可视化与性能分析。
  • 快速定位故障:通过 TraceId 串联全链路,一眼看出哪一环节慢、哪一环节报错。
  • Spring Cloud 官方方案Sleuth 负责自动埋点 + 上下文传播;Zipkin 负责数据收集与 UI 展示。

2. 核心概念

概念说明
Trace一次完整请求的调用链,由唯一的 TraceId 标识。
Span调用链中的基本工作单元(如一次 HTTP 请求、一次 DB 查询、一次 Feign 调用)。每个 Span 有唯一的 SpanId
Parent / Child SpanSpan 之间构成父子关系,如 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. 核心原理

  1. 自动埋点:Sleuth 通过 Spring AOP / 拦截器 / Filter 自动拦截常见的调用组件:
    • HTTPServlet / WebFlux / Gateway
    • RPC:OpenFeign / RestTemplate / WebClient
    • 消息:RabbitMQ / Kafka
    • 数据库:JdbcTemplate / R2DBC(部分支持)
  2. 上下文注入:请求发起时,Sleuth 生成 TraceId + SpanId,通过 HTTP Header 注入下游(如 X-B3-TraceIdX-B3-SpanId)。
  3. 日志集成:TraceId / SpanId 自动写入 MDC,日志输出中可打印 [service-name,trace-id,span-id,exportable]
  4. 数据上报:Span 完成后通过 HTTP / Kafka / RabbitMQ 异步上报到 Zipkin Server。
  5. 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 + ZipkinApache SkyWalkingJaeger
侵入性(加依赖即可自动埋点)无侵入(Java Agent)
编程语言Java 为主多语言(Java/Go/Node.js 等)多语言
功能基础链路 + 耗时分析更丰富(性能剖析、告警、拓扑、日志关联)功能中等
存储MySQL / Elasticsearch / CassandraElasticsearch / H2 / MySQL / TiDB / BanyanDBElasticsearch / 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:latest

2. 基础依赖

每个需要追踪的服务都引入

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=1001

5. 网关层透传 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:9092

Zipkin 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-sleuthSleuth 自动埋点依赖
spring-cloud-sleuth-zipkinZipkin 数据上报依赖
spring.zipkin.base-urlZipkin Server 地址
spring.sleuth.sampler.probability采样率:1.0=100%,0.1=10%
X-B3-TraceIdSleuth 默认透传的 HTTP Header
TracerSleuth 核心 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 看不到 Trace1. 检查 spring.zipkin.base-url 是否正确
2. 检查采样率是否 > 0(probability 默认可能是 0)
3. 检查服务间调用是否通过 Sleuth 支持的组件(Feign/RestTemplate/WebClient),直连 URL 不会传播 TraceId
日志中没有 TraceId1. 确认引入了 spring-cloud-starter-sleuth
2. 确认日志配置包含 %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 不会上报