一、核心概念

1.1 什么是分布式链路追踪?

在微服务架构中,一个用户请求可能经过多个服务(A -> B -> C -> D)。当出现问题(如延迟高、错误)时,难以定位具体发生在哪个服务、哪个环节。分布式链路追踪 (Distributed Tracing) 就是为了解决这个问题。

它通过在一次请求的整个调用链路上生成唯一标识,记录每个服务的处理时间、日志上下文等信息,并将这些信息可视化,帮助开发者:

  • 快速定位性能瓶颈。
  • 诊断错误和异常。
  • 理解系统调用关系。

1.2 Spring Cloud Sleuth 是什么?

  • Sleuth 是 Spring Cloud 提供的库,用于在微服务应用中生成和传播追踪信息
  • 它本身不存储或展示数据,而是负责:
    1. 生成 Trace ID 和 Span ID:为一次请求生成全局唯一的 Trace ID,为每个服务内部的操作生成 Span ID
    2. 注入上下文:将 Trace IDSpan ID 注入到日志和 HTTP 请求头(如 X-B3-TraceId, X-B3-SpanId)中。
    3. 传递上下文:确保这些 ID 在服务间调用时能正确传递(通过 RestTemplate, Feign, WebClient, RabbitMQ 等自动注入)。
    4. 采样 (Sampling):决定哪些请求的追踪数据需要上报(避免全量上报导致性能和存储问题)。

1.3 Zipkin 是什么?

  • Zipkin 是一个开源的分布式追踪系统,由 Twitter 开源。
  • 它负责:
    1. 接收:从 Sleuth 或其他客户端接收追踪数据(通常通过 HTTP、Kafka、RabbitMQ)。
    2. 存储:将数据存储在内存、Cassandra、Elasticsearch 等中。
    3. 查询:提供 API 查询追踪数据。
    4. 展示:提供 Web UI,以可视化的方式(如时间轴图)展示调用链路。

1.4 核心术语

术语 说明
Trace (追踪) 代表一次完整的用户请求(从入口到出口)。由一个全局唯一的 Trace ID 标识。
Span (跨度) 代表 Trace 中的一个逻辑单元或工作单元(如一次 RPC 调用、数据库查询)。每个 Span 有自己唯一的 Span ID
Span ID 当前操作的唯一标识。
Parent Span ID 指向其父 Span 的 ID。根 Span 的 Parent ID 为 null。
Trace ID 标识整个调用链路的唯一 ID,贯穿所有服务。
Annotation (注解) 记录 Span 生命周期中的关键事件,如 cs (Client Send), sr (Server Receive), ss (Server Send), cr (Client Receive)。
B3 Headers Zipkin 使用的一套 HTTP Header 传递追踪信息,如 X-B3-TraceId, X-B3-SpanId, X-B3-ParentSpanId, X-B3-Sampled。Sleuth 会自动处理这些 Header。

1.5 工作流程

  1. 用户请求进入网关或第一个微服务。
  2. Sleuth 生成 Trace ID 和根 Span ID,并将其注入到日志和请求头中。
  3. 该服务调用下一个服务(如通过 RestTemplate)。
  4. Sleuth 自动将 Trace ID, Span ID 等信息从当前请求头复制到下游调用的请求头中。
  5. 下游服务接收到请求,Sleuth 从请求头中提取信息,创建新的 Span(作为子 Span),并继续注入到自己的日志和后续调用中。
  6. 每个服务在处理请求时,会将 Span 数据(包含 Trace ID, Span ID, 服务名、耗时、时间戳等)上报给 Zipkin Server(通过 HTTP、MQ 等)。
  7. Zipkin Server 接收、存储数据。
  8. 开发者通过 Zipkin UI 查询 Trace ID,查看完整的调用链路图。

二、详细操作步骤

步骤 1:部署 Zipkin Server

方法一:使用官方 Jar 包(推荐,简单)

  1. 下载 Zipkin Server Jar

    # 下载最新版本 (检查 https://github.com/openzipkin/zipkin/releases)
    wget -O zipkin-server.jar 'https://search.maven.org/remote_content?g=io.zipkin&a=zipkin-server&v=LATEST&c=exec'
    # 或使用 curl
    curl -sSL https://zipkin.io/quickstart.sh | bash -s
    
  2. 启动 Zipkin Server

    # 使用内存存储 (开发测试)
    java -jar zipkin-server.jar
    
    # 使用 Elasticsearch 存储 (生产推荐)
    # 先启动 Elasticsearch
    # 然后启动 Zipkin
    STORAGE_TYPE=elasticsearch ES_HOSTS=http://localhost:9200 java -jar zipkin-server.jar
    
  3. 访问 Zipkin UI: 启动后,访问 http://localhost:9411,应能看到 Zipkin 的 Web 界面。

方法二:使用 Docker

# 使用内存存储
docker run -d -p 9411:9411 openzipkin/zipkin

# 使用 Elasticsearch (需先启动 ES)
docker run -d -p 9411:9411 \
  -e STORAGE_TYPE=elasticsearch \
  -e ES_HOSTS=http://your-es-host:9200 \
  openzipkin/zipkin

步骤 2:在微服务中集成 Sleuth + Zipkin

2.1 修改所有微服务(包括网关、服务提供者、消费者)

  1. 添加依赖: 在每个需要追踪的微服务的 pom.xml 中添加:

    <!-- Spring Cloud Sleuth 自动集成 Zipkin -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>
    <!-- 如果使用消息队列上报 (如 Kafka/RabbitMQ),需要相应依赖 -->
    <!-- <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency> -->
    
  2. 配置 application.yml: 在每个微服务的配置文件中添加:

    spring:
      application:
        name: order-service # 服务名,会显示在 Zipkin 中
      zipkin:
        # Zipkin Server 的地址
        base-url: http://localhost:9411
        # 上报方式:可选 web (HTTP), kafka, rabbitmq
        sender:
          type: web # 默认就是 web,通过 HTTP 上报
        # 采样率:1.0 表示 100% 采样,生产环境建议降低 (如 0.1)
        # sampler:
        #   probability: 1.0
      # sleuth 配置 (可选)
      sleuth:
        sampler:
          probability: 0.1 # 采样率 10%,减少上报量
    
    # 日志格式 (可选,让日志包含 Trace ID 和 Span ID)
    logging:
      pattern:
        level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
    

    关键配置说明

    • spring.zipkin.base-url必须指向你的 Zipkin Server 地址。
    • spring.zipkin.sender.type:上报方式。web 最简单,kafka/rabbitmq 更可靠,适合生产。
    • spring.sleuth.sampler.probability采样率1.0 全量,0.1 10%。生产环境必须设置,避免性能和存储压力。

2.3 验证集成

  1. 启动所有服务

    • 启动 Zipkin Server。
    • 启动你的微服务(如 gateway-service, order-service, product-service)。
  2. 发起一个跨服务调用: 例如,通过网关调用 order-service,而 order-service 又调用了 product-service

  3. 检查日志: 查看任一服务的日志,应该能看到类似这样的输出:

    INFO [order-service,80e4a55d18209a86,80e4a55d18209a86] Handling order request...
    INFO [product-service,80e4a55d18209a86,2a4b5c6d7e8f9a0b] Fetching product details...
    
    • 80e4a55d18209a86Trace ID,在所有服务的日志中相同
    • 80e4a55d18209a862a4b5c6d7e8f9a0b 是各自的 Span ID
  4. 检查 Zipkin UI

    • 访问 http://localhost:9411
    • 点击 "Find Traces"。
    • 应该能看到最近的追踪记录。
    • 点击一条记录,会展示可视化的调用链路图,显示每个服务的调用顺序、耗时。

三、常见错误与解决方案

错误现象 原因分析 解决方案
Zipkin UI 中无数据 1. spring.zipkin.base-url 配置错误或网络不通
2. 服务未正确添加 spring-cloud-starter-zipkin 依赖
3. 采样率 probability 设置为 0
4. 服务间调用未使用自动注入的客户端 (如原生 HttpURLConnection)
1. 检查 URL 和网络 (curl http://zipkin-host:9411/api/v2/spans)
2. 确认依赖已引入
3. 检查 sleuth.sampler.probability > 0
4. 确保使用 RestTemplate, Feign, WebClient
日志中无 Trace ID/Span ID 1. 未配置日志格式
2. 依赖冲突
1. 添加 logging.pattern.level 配置
2. 检查依赖树,排除冲突的 logging 库
Zipkin Server 启动失败 1. 端口 9411 被占用
2. 内存不足
3. Elasticsearch 连接失败 (如果使用 ES)
1. netstat -anp | grep 9411,更改端口或杀死进程
2. 增加 JVM 内存 (-Xmx1g)
3. 检查 ES 地址、网络、认证
上报延迟或丢失 1. 使用 web 方式,网络不稳定
2. Zipkin Server 处理不过来
3. 采样率过低
1. 生产环境改用 kafkarabbitmq 上报
2. 优化 Zipkin Server 或增加资源
3. 调整采样率或优化
Trace ID 不一致 1. 某个服务未集成 Sleuth
2. 中间有非 Spring Cloud 服务未正确传递 B3 Headers
1. 确保所有相关服务都集成了 Sleuth
2. 检查中间服务是否透传了 X-B3-*

四、注意事项

  1. 采样率 (Sampling)
    • 生产环境必须设置采样率(如 0.010.1),避免全量上报导致性能瓶颈和存储爆炸。
    • 可以根据业务重要性或错误率进行自定义采样策略。
  2. 上报方式
    • 开发/测试:使用 web (HTTP) 简单。
    • 生产环境强烈推荐使用 KafkaRabbitMQ。消息队列提供异步、可靠、解耦的上报,即使 Zipkin Server 短暂不可用,数据也不会丢失。
  3. 服务名 (Service Name)
    • 确保 spring.application.name 设置合理且唯一,它会显示在 Zipkin UI 中。
  4. B3 Headers 传递
    • Sleuth 会自动处理基于 RestTemplate, Feign, WebClient, Ribbon, Hystrix, Spring Cloud Stream 等的调用。
    • 如果使用原生 HTTP 客户端或其他技术栈(如 Node.js),需要手动提取和传递 X-B3-* 头。
  5. Zipkin 存储
    • 内存存储:仅用于测试,重启数据丢失。
    • 生产环境:必须使用持久化存储,如 Elasticsearch (最常用)、Cassandra、MySQL。
  6. 性能影响
    • Sleuth 本身开销很小,但全量上报对网络和 Zipkin Server 有压力。合理设置采样率是关键。
  7. 版本兼容性
    • 确保 Spring Boot、Spring Cloud、Sleuth/Zipkin 版本兼容。

五、使用技巧

5.1 自定义采样策略

// CustomSamplerConfig.java
@Configuration
public class CustomSamplerConfig {

    @Bean
    public Sampler customSampler() {
        return Sampler.builder()
            .rate(5) // 每秒最多采样5个请求
            // 或者使用 PredicateBasedSampler
            // .withRule(SamplingRule.builder()
            //     .traceId128Bit(true)
            //     .build())
            .build();
    }

    // 或者更复杂的策略
    // @Bean
    // public AlwaysSampler alwaysSampler() {
    //     return new AlwaysSampler(); // 100% 采样
    // }
}

5.2 使用消息队列上报 (Kafka)

  1. 添加 Kafka 依赖

    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    
  2. 配置 application.yml

    spring:
      zipkin:
        sender:
          type: kafka # 使用 Kafka 上报
        kafka:
          topic: zipkin # Kafka topic 名称
      kafka:
        bootstrap-servers: localhost:9092
        producer:
          key-serializer: org.apache.kafka.common.serialization.StringSerializer
          value-serializer: org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
          properties:
            value.deserializer: brave.kafka.serializers.TracingSerializer # Sleuth 提供的序列化器
      sleuth:
        sampler:
          probability: 0.1
    

    注意value.deserializer 配置是关键,确保 Kafka Consumer 能正确反序列化 Span 数据。

  3. 启动 Kafka,并确保 topic zipkin 存在。

5.3 在非 WebFlux 服务中使用

Sleuth 支持传统 MVC (spring-boot-starter-web) 和响应式 (spring-boot-starter-webflux)。

5.4 结合日志系统 (如 ELK)

  • 将包含 Trace IDSpan ID 的日志发送到 ELK (Elasticsearch, Logstash, Kibana)。
  • 在 Kibana 中,可以通过 Trace ID 关联查询一次请求在所有服务中的日志,实现“日志 + 链路”的一体化排查。

5.5 自定义 Span

@RestController
public class OrderController {

    @Autowired
    private Tracer tracer; // 注入 Tracer

    @GetMapping("/orders/{id}")
    public String getOrder(@PathVariable String id) {
        // 创建一个子 Span
        Span customSpan = tracer.nextSpan().name("custom-processing").start();
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(customSpan)) {
            // 在此代码块内执行的任何 Sleuth 操作都会关联到 customSpan
            // 模拟一些耗时操作
            Thread.sleep(100); // 注意:生产环境避免阻塞,这里仅为演示
            customSpan.tag("item.id", id); // 添加自定义标签
            return "Order: " + id;
        } catch (Exception e) {
            customSpan.error(e); // 标记错误
            throw e;
        } finally {
            customSpan.finish(); // 结束 Span
        }
    }
}

六、最佳实践

  1. 全链路覆盖:确保所有微服务(包括网关、中间件调用)都集成 Sleuth。
  2. 生产环境使用消息队列Kafka/RabbitMQ 是生产环境上报的黄金标准。
  3. 合理设置采样率:平衡监控需求和系统开销。可结合错误率进行动态采样。
  4. 使用持久化存储:Zipkin Server 使用 Elasticsearch 存储数据。
  5. 统一服务命名:清晰的服务名便于在 UI 中识别。
  6. 结合日志:在日志中打印 Trace ID,实现链路与日志的联动排查。
  7. 监控 Zipkin:监控 Zipkin Server 的健康状况、消息积压、存储空间。
  8. 权限控制:为 Zipkin UI 配置访问认证(如 Spring Security)。
  9. 数据保留策略:根据存储容量设置合理的数据过期时间。

七、性能优化

  1. 采样
    • 这是最有效的优化手段。将采样率从 1.0 降到 0.1,上报数据量直接减少 90%。
  2. 上报方式
    • 使用 异步消息队列 (Kafka/RabbitMQ),避免 HTTP 同步上报阻塞业务线程。
  3. Zipkin Server 优化
    • 存储:使用 SSD 磁盘的 Elasticsearch 集群。
    • 索引:合理设置 Elasticsearch 的索引策略和分片。
    • 资源:为 Zipkin Server 分配足够的 CPU 和内存。
  4. 减少 Span 数据
    • 避免在 Span 上添加过多的自定义 tagevent
  5. 网络
    • 确保微服务与 Zipkin Server (或 Kafka) 之间的网络延迟低。
  6. 客户端配置
    • Sleuth 客户端本身开销极小,无需特殊优化。重点在于控制上报频率和方式。

总结

Spring Cloud Sleuth + Zipkin 是一套成熟、易用的分布式链路追踪解决方案。

核心步骤

  1. 部署:启动 Zipkin Server (推荐 Docker 或 Jar)。
  2. 集成:在所有微服务中添加 spring-cloud-starter-zipkin 依赖。
  3. 配置:设置 spring.zipkin.base-urlspring.sleuth.sampler.probability
  4. 验证:发起请求,检查日志中的 Trace ID 和 Zipkin UI 中的链路图。

关键注意事项

  • 生产环境必须使用消息队列 (Kafka/RabbitMQ) 上报
  • 务必设置合理的采样率
  • 确保服务名清晰,B3 Headers 正确传递

掌握链路追踪,是保障微服务系统可观测性(Observability)的关键一步,能极大提升问题定位和性能优化的效率。