一、核心概念
1.1 什么是分布式链路追踪?
在微服务架构中,一个用户请求可能经过多个服务(A -> B -> C -> D)。当出现问题(如延迟高、错误)时,难以定位具体发生在哪个服务、哪个环节。分布式链路追踪 (Distributed Tracing) 就是为了解决这个问题。
它通过在一次请求的整个调用链路上生成唯一标识,记录每个服务的处理时间、日志上下文等信息,并将这些信息可视化,帮助开发者:
- 快速定位性能瓶颈。
- 诊断错误和异常。
- 理解系统调用关系。
1.2 Spring Cloud Sleuth 是什么?
- Sleuth 是 Spring Cloud 提供的库,用于在微服务应用中生成和传播追踪信息。
- 它本身不存储或展示数据,而是负责:
- 生成 Trace ID 和 Span ID:为一次请求生成全局唯一的
Trace ID
,为每个服务内部的操作生成Span ID
。 - 注入上下文:将
Trace ID
和Span ID
注入到日志和 HTTP 请求头(如X-B3-TraceId
,X-B3-SpanId
)中。 - 传递上下文:确保这些 ID 在服务间调用时能正确传递(通过
RestTemplate
,Feign
,WebClient
,RabbitMQ
等自动注入)。 - 采样 (Sampling):决定哪些请求的追踪数据需要上报(避免全量上报导致性能和存储问题)。
- 生成 Trace ID 和 Span ID:为一次请求生成全局唯一的
1.3 Zipkin 是什么?
- Zipkin 是一个开源的分布式追踪系统,由 Twitter 开源。
- 它负责:
- 接收:从 Sleuth 或其他客户端接收追踪数据(通常通过 HTTP、Kafka、RabbitMQ)。
- 存储:将数据存储在内存、Cassandra、Elasticsearch 等中。
- 查询:提供 API 查询追踪数据。
- 展示:提供 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 工作流程
- 用户请求进入网关或第一个微服务。
- Sleuth 生成
Trace ID
和根Span ID
,并将其注入到日志和请求头中。 - 该服务调用下一个服务(如通过
RestTemplate
)。 - Sleuth 自动将
Trace ID
,Span ID
等信息从当前请求头复制到下游调用的请求头中。 - 下游服务接收到请求,Sleuth 从请求头中提取信息,创建新的
Span
(作为子 Span),并继续注入到自己的日志和后续调用中。 - 每个服务在处理请求时,会将
Span
数据(包含Trace ID
,Span ID
, 服务名、耗时、时间戳等)上报给 Zipkin Server(通过 HTTP、MQ 等)。 - Zipkin Server 接收、存储数据。
- 开发者通过 Zipkin UI 查询
Trace ID
,查看完整的调用链路图。
二、详细操作步骤
步骤 1:部署 Zipkin Server
方法一:使用官方 Jar 包(推荐,简单)
下载 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
启动 Zipkin Server:
# 使用内存存储 (开发测试) java -jar zipkin-server.jar # 使用 Elasticsearch 存储 (生产推荐) # 先启动 Elasticsearch # 然后启动 Zipkin STORAGE_TYPE=elasticsearch ES_HOSTS=http://localhost:9200 java -jar zipkin-server.jar
访问 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 修改所有微服务(包括网关、服务提供者、消费者)
添加依赖: 在每个需要追踪的微服务的
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> -->
配置
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 验证集成
启动所有服务:
- 启动 Zipkin Server。
- 启动你的微服务(如
gateway-service
,order-service
,product-service
)。
发起一个跨服务调用: 例如,通过网关调用
order-service
,而order-service
又调用了product-service
。检查日志: 查看任一服务的日志,应该能看到类似这样的输出:
INFO [order-service,80e4a55d18209a86,80e4a55d18209a86] Handling order request... INFO [product-service,80e4a55d18209a86,2a4b5c6d7e8f9a0b] Fetching product details...
80e4a55d18209a86
是Trace ID
,在所有服务的日志中相同。80e4a55d18209a86
和2a4b5c6d7e8f9a0b
是各自的Span ID
。
检查 Zipkin UI:
- 访问
http://localhost:9411
。 - 点击 "Find Traces"。
- 应该能看到最近的追踪记录。
- 点击一条记录,会展示可视化的调用链路图,显示每个服务的调用顺序、耗时。
- 访问
三、常见错误与解决方案
错误现象 | 原因分析 | 解决方案 |
---|---|---|
Zipkin UI 中无数据 | 1. spring.zipkin.base-url 配置错误或网络不通2. 服务未正确添加 spring-cloud-starter-zipkin 依赖3. 采样率 probability 设置为 04. 服务间调用未使用自动注入的客户端 (如原生 HttpURLConnection ) |
1. 检查 URL 和网络 (curl http://zipkin-host:9411/api/v2/spans )2. 确认依赖已引入 3. 检查 sleuth.sampler.probability > 04. 确保使用 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. 生产环境改用 kafka 或 rabbitmq 上报2. 优化 Zipkin Server 或增加资源 3. 调整采样率或优化 |
Trace ID 不一致 | 1. 某个服务未集成 Sleuth 2. 中间有非 Spring Cloud 服务未正确传递 B3 Headers |
1. 确保所有相关服务都集成了 Sleuth 2. 检查中间服务是否透传了 X-B3-* 头 |
四、注意事项
- 采样率 (Sampling):
- 生产环境必须设置采样率(如
0.01
到0.1
),避免全量上报导致性能瓶颈和存储爆炸。 - 可以根据业务重要性或错误率进行自定义采样策略。
- 生产环境必须设置采样率(如
- 上报方式:
- 开发/测试:使用
web
(HTTP) 简单。 - 生产环境:强烈推荐使用
Kafka
或RabbitMQ
。消息队列提供异步、可靠、解耦的上报,即使 Zipkin Server 短暂不可用,数据也不会丢失。
- 开发/测试:使用
- 服务名 (Service Name):
- 确保
spring.application.name
设置合理且唯一,它会显示在 Zipkin UI 中。
- 确保
- B3 Headers 传递:
- Sleuth 会自动处理基于
RestTemplate
,Feign
,WebClient
,Ribbon
,Hystrix
,Spring Cloud Stream
等的调用。 - 如果使用原生 HTTP 客户端或其他技术栈(如 Node.js),需要手动提取和传递
X-B3-*
头。
- Sleuth 会自动处理基于
- Zipkin 存储:
- 内存存储:仅用于测试,重启数据丢失。
- 生产环境:必须使用持久化存储,如 Elasticsearch (最常用)、Cassandra、MySQL。
- 性能影响:
- Sleuth 本身开销很小,但全量上报对网络和 Zipkin Server 有压力。合理设置采样率是关键。
- 版本兼容性:
- 确保 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)
添加 Kafka 依赖:
<dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>
配置
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 数据。启动 Kafka,并确保 topic
zipkin
存在。
5.3 在非 WebFlux 服务中使用
Sleuth 支持传统 MVC (spring-boot-starter-web
) 和响应式 (spring-boot-starter-webflux
)。
5.4 结合日志系统 (如 ELK)
- 将包含
Trace ID
和Span 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
}
}
}
六、最佳实践
- 全链路覆盖:确保所有微服务(包括网关、中间件调用)都集成 Sleuth。
- 生产环境使用消息队列:
Kafka
/RabbitMQ
是生产环境上报的黄金标准。 - 合理设置采样率:平衡监控需求和系统开销。可结合错误率进行动态采样。
- 使用持久化存储:Zipkin Server 使用 Elasticsearch 存储数据。
- 统一服务命名:清晰的服务名便于在 UI 中识别。
- 结合日志:在日志中打印
Trace ID
,实现链路与日志的联动排查。 - 监控 Zipkin:监控 Zipkin Server 的健康状况、消息积压、存储空间。
- 权限控制:为 Zipkin UI 配置访问认证(如 Spring Security)。
- 数据保留策略:根据存储容量设置合理的数据过期时间。
七、性能优化
- 采样:
- 这是最有效的优化手段。将采样率从
1.0
降到0.1
,上报数据量直接减少 90%。
- 这是最有效的优化手段。将采样率从
- 上报方式:
- 使用 异步消息队列 (
Kafka
/RabbitMQ
),避免 HTTP 同步上报阻塞业务线程。
- 使用 异步消息队列 (
- Zipkin Server 优化:
- 存储:使用 SSD 磁盘的 Elasticsearch 集群。
- 索引:合理设置 Elasticsearch 的索引策略和分片。
- 资源:为 Zipkin Server 分配足够的 CPU 和内存。
- 减少 Span 数据:
- 避免在 Span 上添加过多的自定义
tag
或event
。
- 避免在 Span 上添加过多的自定义
- 网络:
- 确保微服务与 Zipkin Server (或 Kafka) 之间的网络延迟低。
- 客户端配置:
- Sleuth 客户端本身开销极小,无需特殊优化。重点在于控制上报频率和方式。
总结
Spring Cloud Sleuth + Zipkin 是一套成熟、易用的分布式链路追踪解决方案。
核心步骤:
- 部署:启动 Zipkin Server (推荐 Docker 或 Jar)。
- 集成:在所有微服务中添加
spring-cloud-starter-zipkin
依赖。 - 配置:设置
spring.zipkin.base-url
和spring.sleuth.sampler.probability
。 - 验证:发起请求,检查日志中的
Trace ID
和 Zipkin UI 中的链路图。
关键注意事项:
- 生产环境必须使用消息队列 (Kafka/RabbitMQ) 上报。
- 务必设置合理的采样率。
- 确保服务名清晰,B3 Headers 正确传递。
掌握链路追踪,是保障微服务系统可观测性(Observability)的关键一步,能极大提升问题定位和性能优化的效率。