Spring Cloud Sleuth + Zipkin 微服务链路追踪实战:代码配置与可视化详解
一、技术选型与环境准备
二、项目结构设计
三、Zipkin Server 搭建
四、微服务改造
五、启动所有服务并测试
六、深入理解 Sleuth 和 Zipkin 的工作原理
七、高级配置与优化
八、总结
在微服务架构中,服务之间的调用关系错综复杂,一旦出现问题,排查起来犹如大海捞针。这时,链路追踪技术就显得尤为重要。Spring Cloud Sleuth 和 Zipkin 是目前流行的链路追踪解决方案,可以帮助我们清晰地了解请求在微服务之间的调用路径和耗时情况,从而快速定位问题。本文将以一个完整的示例,详细介绍如何使用 Spring Cloud Sleuth 和 Zipkin 来追踪微服务架构中的请求链路,并通过可视化界面查看调用关系和耗时情况。
一、技术选型与环境准备
- Spring Boot 版本:2.7.x 或更高版本(推荐最新稳定版)
- Spring Cloud 版本:2021.0.x 或更高版本(与 Spring Boot 版本对应)
- Zipkin 版本:2.23.x 或更高版本
- JDK 版本:1.8 或更高版本
- 构建工具:Maven 或 Gradle
- IDE:IntelliJ IDEA 或 Eclipse
二、项目结构设计
为了演示方便,我们创建一个包含三个微服务的简单应用:
- service-a:接收外部请求,调用 service-b
- service-b:接收 service-a 的请求,调用 service-c
- service-c:接收 service-b 的请求,返回最终结果
- zipkin-server: 负责收集和展示链路数据
项目结构如下:
microservice-tracing/ ├── service-a/ │ ├── pom.xml │ └── src/main/java/... ├── service-b/ │ ├── pom.xml │ └── src/main/java/... ├── service-c/ │ ├── pom.xml │ └── src/main/java/... └── zipkin-server/ ├── pom.xml └── src/main/java/...
三、Zipkin Server 搭建
创建 zipkin-server 项目
创建一个 Spring Boot 项目,命名为
zipkin-server
。添加依赖
在
pom.xml
中添加以下依赖:<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 确保
spring-cloud.version
与你的 Spring Cloud 版本一致。配置 Zipkin Server
在
application.properties
或application.yml
中添加以下配置:server.port=9411 spring.application.name=zipkin-server # 使用内存存储,生产环境建议使用持久化存储(如 Elasticsearch, Cassandra) zipkin.storage.type=mem
启动 Zipkin Server
在主类上添加
@EnableZipkinServer
注解,启动 Zipkin Server。import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import zipkin2.server.internal.EnableZipkinServer; @SpringBootApplication @EnableZipkinServer public class ZipkinServerApplication { public static void main(String[] args) { SpringApplication.run(ZipkinServerApplication.class, args); } } 启动成功后,访问
http://localhost:9411
,即可看到 Zipkin 的 Web UI。
四、微服务改造
对 service-a
、service-b
和 service-c
进行如下改造:
创建 Spring Boot 项目
分别创建三个 Spring Boot 项目,命名为
service-a
、service-b
和service-c
。添加依赖
在每个项目的
pom.xml
中添加以下依赖:<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> spring-boot-starter-web
:提供 Web 服务能力spring-cloud-starter-sleuth
:链路追踪的核心组件spring-cloud-starter-zipkin
:将链路数据发送到 Zipkin Serverspring-cloud-starter-openfeign
:声明式 HTTP 客户端,方便服务间调用
配置微服务
在每个项目的
application.properties
或application.yml
中添加以下配置:service-a:
server.port=8081 spring.application.name=service-a # Zipkin Server 地址 spring.zipkin.baseUrl=http://localhost:9411 # 采样率,1.0 表示全部采样 spring.sleuth.sampler.probability=1.0
service-b:
server.port=8082 spring.application.name=service-b spring.zipkin.baseUrl=http://localhost:9411 spring.sleuth.sampler.probability=1.0
service-c:
server.port=8083 spring.application.name=service-c spring.zipkin.baseUrl=http://localhost:9411 spring.sleuth.sampler.probability=1.0
server.port
:服务端口spring.application.name
:服务名称,用于在 Zipkin UI 中区分服务spring.zipkin.baseUrl
:Zipkin Server 的地址spring.sleuth.sampler.probability
:采样率,控制链路数据的采集比例。1.0
表示全部采样,生产环境可以根据实际情况调整,避免数据量过大。
使用 Feign 进行服务间调用
a. 定义 Feign 客户端接口
在
service-a
中定义一个 Feign 客户端接口,用于调用service-b
:import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient("service-b") public interface ServiceBClient { @GetMapping("/b") String callB(); } @FeignClient("service-b")
:指定要调用的服务名称,需要与service-b
的spring.application.name
一致。@GetMapping("/b")
:指定要调用的接口路径,需要与service-b
的 Controller 接口路径一致。
同样,在
service-b
中定义一个 Feign 客户端接口,用于调用service-c
:import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient("service-c") public interface ServiceCClient { @GetMapping("/c") String callC(); } b. 启用 Feign 客户端
在
service-a
和service-b
的启动类上添加@EnableFeignClients
注解,启用 Feign 客户端。import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class ServiceAApplication { public static void main(String[] args) { SpringApplication.run(ServiceAApplication.class, args); } } c. 注入 Feign 客户端并调用
在
service-a
的 Controller 中注入ServiceBClient
并调用:import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ServiceAController { @Autowired private ServiceBClient serviceBClient; @GetMapping("/a") public String callA() { return "Service A -> " + serviceBClient.callB(); } } 在
service-b
的 Controller 中注入ServiceCClient
并调用:import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ServiceBController { @Autowired private ServiceCClient serviceCClient; @GetMapping("/b") public String callB() { return "Service B -> " + serviceCClient.callC(); } } 创建 Controller
在
service-c
中创建一个简单的 Controller,返回一个字符串:import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ServiceCController { @GetMapping("/c") public String callC() { return "Service C"; } }
五、启动所有服务并测试
启动 Zipkin Server
启动 service-a、service-b 和 service-c
访问 service-a 的接口
在浏览器中访问
http://localhost:8081/a
,如果一切正常,应该看到类似如下的输出:Service A -> Service B -> Service C
查看 Zipkin UI
访问
http://localhost:9411
,点击 “Find Traces” 按钮,即可看到链路追踪数据。在 Zipkin UI 中,你可以看到:
- 请求在各个微服务之间的调用关系
- 每个调用的耗时情况
- 请求的详细信息,如 HTTP 方法、URL、Headers 等
六、深入理解 Sleuth 和 Zipkin 的工作原理
- Sleuth:负责生成和传播 Trace ID 和 Span ID。Trace ID 标识一个完整的请求链路,Span ID 标识链路中的一个调用单元。Sleuth 会自动将 Trace ID 和 Span ID 添加到 HTTP Headers 中,以便在服务间传递。
- Zipkin:负责收集、存储和展示链路数据。Zipkin Agent 负责将链路数据发送到 Zipkin Collector,Zipkin Collector 将数据存储到 Zipkin Storage 中,Zipkin UI 从 Zipkin Storage 中读取数据并展示。
七、高级配置与优化
持久化存储
默认情况下,Zipkin Server 使用内存存储,数据会丢失。生产环境建议使用持久化存储,如 Elasticsearch, Cassandra 等。可以通过配置
zipkin.storage.type
来指定存储类型,并配置相应的连接信息。例如,使用 Elasticsearch 存储:
zipkin.storage.type=elasticsearch spring.elasticsearch.rest.uris=http://localhost:9200
调整采样率
spring.sleuth.sampler.probability
用于控制采样率。如果微服务数量较多,请求量较大,可以适当降低采样率,避免数据量过大。但需要注意的是,采样率过低可能会导致部分链路数据丢失。自定义 Span
Sleuth 默认会追踪 HTTP 请求、数据库操作等。如果需要追踪其他操作,可以使用
@NewSpan
注解自定义 Span。import org.springframework.cloud.sleuth.annotation.NewSpan; public class MyService { @NewSpan("myCustomSpan") public void doSomething() { // ... } }
八、总结
本文详细介绍了如何使用 Spring Cloud Sleuth 和 Zipkin 来追踪微服务架构中的请求链路,并通过可视化界面查看调用关系和耗时情况。链路追踪是微服务架构中不可或缺的一部分,可以帮助我们快速定位问题,提高系统的可维护性和可观测性。希望本文能够帮助你更好地理解和使用 Spring Cloud Sleuth 和 Zipkin。
通过本文的示例,你应该能够:
- 搭建 Zipkin Server
- 配置微服务,集成 Spring Cloud Sleuth 和 Zipkin
- 使用 Feign 进行服务间调用
- 在 Zipkin UI 中查看链路追踪数据
- 理解 Sleuth 和 Zipkin 的工作原理
- 进行高级配置与优化,如持久化存储、调整采样率、自定义 Span
掌握这些知识,你就可以在自己的微服务项目中应用链路追踪技术,提升系统的可观测性和可维护性。