本文目录导读:

- 项目结构
- 父POM配置文件
- 服务A的实现
- 服务B的实现
- 服务C的实现
- 配置文件
- 运行Docker Zipkin
- 启动和测试
- 查看链路追踪
- 日志输出示例
- 自定义链路追踪(高级用法)
- 使用Spring Cloud Gateway集成Zipkin
- 注意事项
我将为您提供一个完整的Java链路追踪案例,使用Spring Cloud Sleuth和Zipkin实现分布式系统的链路追踪。
项目结构
tracing-demo/
├── pom.xml (父POM)
├── service-a/ # 服务A
├── service-b/ # 服务B
└── service-c/ # 服务C
父POM配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>tracing-demo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>
<modules>
<module>service-a</module>
<module>service-b</module>
<module>service-c</module>
</modules>
<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>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
服务A的实现
ServiceAApplication.java
package com.example.servicea;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@RestController
public class ServiceAApplication {
private static final Logger logger = LoggerFactory.getLogger(ServiceAApplication.class);
@Autowired
private RestTemplate restTemplate;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@GetMapping("/api/service-a")
public String serviceA() {
logger.info("Service A received request");
// 模拟业务处理
String result = "Service A processed. ";
// 调用Service B
logger.info("Service A calling Service B");
String serviceBResponse = restTemplate.getForObject(
"http://localhost:8081/api/service-b", String.class);
result += serviceBResponse;
logger.info("Service A completed request");
return result;
}
public static void main(String[] args) {
SpringApplication.run(ServiceAApplication.class, args);
}
}
服务B的实现
ServiceBApplication.java
package com.example.serviceb;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@RestController
public class ServiceBApplication {
private static final Logger logger = LoggerFactory.getLogger(ServiceBApplication.class);
@Autowired
private RestTemplate restTemplate;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@GetMapping("/api/service-b")
public String serviceB() throws InterruptedException {
logger.info("Service B received request");
// 模拟耗时操作
Thread.sleep(100);
// 调用Service C
logger.info("Service B calling Service C");
String serviceCResponse = restTemplate.getForObject(
"http://localhost:8082/api/service-c", String.class);
logger.info("Service B completed request");
return "Service B processed. " + serviceCResponse;
}
public static void main(String[] args) {
SpringApplication.run(ServiceBApplication.class, args);
}
}
服务C的实现
ServiceCApplication.java
package com.example.servicec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class ServiceCApplication {
private static final Logger logger = LoggerFactory.getLogger(ServiceCApplication.class);
@GetMapping("/api/service-c")
public String serviceC() throws InterruptedException {
logger.info("Service C received request");
// 模拟耗时操作
Thread.sleep(200);
logger.info("Service C completed request");
return "Service C processed";
}
public static void main(String[] args) {
SpringApplication.run(ServiceCApplication.class, args);
}
}
配置文件
service-a/src/main/resources/application.yml
server:
port: 8080
spring:
application:
name: service-a
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0 # 采样率,1.0表示100%采样
logging:
level:
org.springframework.web: INFO
com.example: DEBUG
service-b/src/main/resources/application.yml
server:
port: 8081
spring:
application:
name: service-b
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0
logging:
level:
org.springframework.web: INFO
com.example: DEBUG
service-c/src/main/resources/application.yml
server:
port: 8082
spring:
application:
name: service-c
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0
logging:
level:
org.springframework.web: INFO
com.example: DEBUG
运行Docker Zipkin
# 运行Zipkin服务 docker run -d -p 9411:9411 openzipkin/zipkin
启动和测试
启动三个服务
# 启动Service A mvn spring-boot:run -pl service-a # 启动Service B mvn spring-boot:run -pl service-b # 启动Service C mvn spring-boot:run -pl service-c
测试请求
curl http://localhost:8080/api/service-a
预期输出
Service A processed. Service B processed. Service C processed
查看链路追踪
- 访问Zipkin UI:
http://localhost:9411 - 点击"Find Traces"查看所有链路
- 可以看到完整的调用链路:Service A → Service B → Service C
日志输出示例
2024-01-15 10:30:45.123 [service-a,3f4a5b6c7d8e9f0a,3f4a5b6c7d8e9f0a,true]
INFO com.example.servicea.ServiceAApplication - Service A received request
2024-01-15 10:30:45.200 [service-b,3f4a5b6c7d8e9f0a,2e8f0f1a2b3c4d5e,true]
INFO com.example.serviceb.ServiceBApplication - Service B received request
2024-01-15 10:30:45.300 [service-c,3f4a5b6c7d8e9f0a,5a6b7c8d9e0f1a2b,true]
INFO com.example.servicec.ServiceCApplication - Service C received request
其中日志格式为:[应用名, TraceId, SpanId, 是否导出]
自定义链路追踪(高级用法)
自定义Span创建
package com.example.servicea;
import brave.Span;
import brave.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CustomTracingController {
@Autowired
private Tracer tracer;
@GetMapping("/custom-trace")
public String customTrace() {
// 创建自定义Span
Span customSpan = tracer.nextSpan().name("custom-operation").start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(customSpan)) {
// 添加自定义标签
customSpan.tag("custom.tag", "custom-value");
customSpan.annotate("custom-annotation");
// 业务逻辑
Thread.sleep(500);
return "Custom tracing completed";
} catch (Exception e) {
customSpan.error(e);
return "Error occurred";
} finally {
customSpan.finish();
}
}
}
使用Spring Cloud Gateway集成Zipkin
Gateway配置
spring:
application:
name: api-gateway
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0
cloud:
gateway:
routes:
- id: service-a
uri: http://localhost:8080
predicates:
- Path=/api/service-a/**
注意事项
- 采样率设置:生产环境建议设置为小于1的值(如0.1),减少性能损耗
- 异步处理:使用
@Async或消息队列时,需要额外配置以保证链路追踪的传递 - 多线程传播:使用
ExecutorService时,需要手动传播trace context - 数据库调用:可以通过集成
spring-cloud-sleuth-zipkin的JDBC拦截器来追踪数据库操作 - 生产环境:考虑使用ELK等日志系统收集日志,配合Zipkin实现完整的监控体系
这个案例展示了完整的分布式链路追踪实现,可以帮助您快速定位分布式系统中的性能瓶颈和故障点。