本文目录导读:

我将为您提供一个完整的Spring Cloud Sleuth链路追踪案例,包含核心配置和实战示例。
项目架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 服务A │────▶│ 服务B │────▶│ 服务C │
│ (前端服务) │ │ (业务服务) │ │ (数据服务) │
└─────────────┘ └─────────────┘ └─────────────┘
核心依赖配置
1 父POM文件 (pom.xml)
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>tracing-demo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
<spring-cloud-sleuth.version>3.1.8</spring-cloud-sleuth.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>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- Zipkin 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>${spring-cloud-sleuth.version}</version>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
服务A配置和代码
1 服务A配置 (service-a/src/main/resources/application.yml)
server:
port: 8081
spring:
application:
name: service-a
sleuth:
sampler:
probability: 1.0 # 采样率,1.0表示100%采样
zipkin:
base-url: http://localhost:9411 # Zipkin服务器地址
zipkin:
base-url: http://localhost:9411
management:
endpoints:
web:
exposure:
include: health,info,metrics
metrics:
export:
zipkin:
enabled: true
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss:SSS} [%thread] %-5level %logger{50} - %msg%n"
2 服务A主类 (service-a/src/main/java/com/example/ServiceAApplication.java)
package com.example;
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);
}
}
3 服务A控制器 (service-a/src/main/java/com/example/controller/TraceController.java)
package com.example.controller;
import com.example.client.ServiceBClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/a")
public class TraceController {
@Autowired
private ServiceBClient serviceBClient;
@Autowired
private RestTemplate restTemplate;
/**
* 使用Feign调用服务B
*/
@GetMapping("/feign/trace")
public Map<String, Object> feignTrace() {
log.info("Service A - Feign调用开始");
// 调用服务B
Map<String, Object> result = serviceBClient.callServiceB();
log.info("Service A - Feign调用结束");
return buildResponse("service-a", result);
}
/**
* 使用RestTemplate调用服务B
*/
@GetMapping("/rest/trace")
public Map<String, Object> restTrace() {
log.info("Service A - RestTemplate调用开始");
// 使用RestTemplate调用服务B
String url = "http://localhost:8082/api/b/process";
Map<String, Object> result = restTemplate.getForObject(url, Map.class);
log.info("Service A - RestTemplate调用结束");
return buildResponse("service-a", result);
}
/**
* 构建统一响应
*/
private Map<String, Object> buildResponse(String currentService, Map<String, Object> downstreamResult) {
Map<String, Object> response = new HashMap<>();
response.put("currentService", currentService);
response.put("timestamp", System.currentTimeMillis());
response.put("downstreamResult", downstreamResult);
return response;
}
}
4 服务A的Feign客户端 (service-a/src/main/java/com/example/client/ServiceBClient.java)
package com.example.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Map;
@FeignClient(name = "service-b", url = "http://localhost:8082")
public interface ServiceBClient {
@GetMapping("/api/b/process")
Map<String, Object> callServiceB();
}
5 服务A配置类 (service-a/src/main/java/com/example/config/RestTemplateConfig.java)
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
服务B配置和代码
1 服务B配置 (service-b/src/main/resources/application.yml)
server:
port: 8082
spring:
application:
name: service-b
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411
zipkin:
base-url: http://localhost:9411
management:
endpoints:
web:
exposure:
include: health,info,metrics
2 服务B控制器 (service-b/src/main/java/com/example/controller/TraceController.java)
package com.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.cloud.sleuth.Tracer;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/b")
public class TraceController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private Tracer tracer;
/**
* 处理来自服务A的请求
*/
@GetMapping("/process")
public Map<String, Object> process() {
log.info("Service B - 收到请求");
// 获取当前Span信息
String traceId = tracer.currentSpan() != null ?
tracer.currentSpan().context().traceId() : "null";
String spanId = tracer.currentSpan() != null ?
tracer.currentSpan().context().spanId() : "null";
log.info("Service B - TraceId: {}, SpanId: {}", traceId, spanId);
// 异步任务测试
asyncProcess();
// 调用服务C
Map<String, Object> result = callServiceC();
log.info("Service B - 处理完毕");
return buildResponse(traceId, spanId, result);
}
/**
* 调用服务C
*/
private Map<String, Object> callServiceC() {
log.info("Service B - 调用服务C");
String url = "http://localhost:8083/api/c/process";
Map<String, Object> result = restTemplate.getForObject(url, Map.class);
log.info("Service B - 服务C调用完成");
return result;
}
/**
* 异步处理任务
*/
private void asyncProcess() {
new Thread(() -> {
log.info("Service B - 异步任务开始");
// 模拟异步处理
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.info("Service B - 异步任务结束");
}).start();
}
/**
* 构建响应
*/
private Map<String, Object> buildResponse(String traceId, String spanId,
Map<String, Object> result) {
Map<String, Object> response = new HashMap<>();
response.put("currentService", "service-b");
response.put("traceId", traceId);
response.put("spanId", spanId);
response.put("downstreamResult", result);
return response;
}
}
服务C配置和代码
1 服务C配置 (service-c/src/main/resources/application.yml)
server:
port: 8083
spring:
application:
name: service-c
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411
zipkin:
base-url: http://localhost:9411
management:
endpoints:
web:
exposure:
include: health,info,metrics
2 服务C控制器 (service-c/src/main/java/com/example/controller/TraceController.java)
package com.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.cloud.sleuth.Tracer;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/c")
public class TraceController {
@Autowired
private Tracer tracer;
/**
* 处理来自服务B的请求
*/
@GetMapping("/process")
public Map<String, Object> process() {
log.info("Service C - 开始处理请求");
// 模拟数据处理
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 获取Trace信息
String traceId = tracer.currentSpan() != null ?
tracer.currentSpan().context().traceId() : "null";
String spanId = tracer.currentSpan() != null ?
tracer.currentSpan().context().spanId() : "null";
log.info("Service C - 处理完成, TraceId: {}, SpanId: {}", traceId, spanId);
Map<String, Object> response = new HashMap<>();
response.put("currentService", "service-c");
response.put("traceId", traceId);
response.put("spanId", spanId);
response.put("data", "Service C processed data");
response.put("timestamp", System.currentTimeMillis());
return response;
}
}
自定义Span示例 (服务B添加)
package com.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TraceInterceptor {
@Autowired
private Tracer tracer;
/**
* 创建自定义Span
*/
public void createCustomSpan() {
Span customSpan = tracer.nextSpan().name("custom-business-operation").start();
try (Tracer.SpanInScope ws = tracer.withSpan(customSpan)) {
log.info("执行自定义业务逻辑");
// 添加自定义标签
customSpan.tag("business.type", "order");
customSpan.tag("business.status", "success");
// 添加事件
customSpan.event("business.start");
// 模拟业务处理
Thread.sleep(500);
customSpan.event("business.end");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
customSpan.end();
}
}
}
Docker Compose部署Zipkin
version: '3.8'
services:
zipkin:
image: openzipkin/zipkin:2.24
container_name: zipkin
ports:
- "9411:9411"
environment:
- JAVA_OPTS=-Xmx512m
networks:
- tracing-network
# Elasticsearch用于存储追踪数据(可选)
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms256m -Xmx256m
ports:
- "9200:9200"
networks:
- tracing-network
networks:
tracing-network:
driver: bridge
启动和测试
1 启动Zipkin
docker-compose up -d
2 启动服务
# 分别启动服务A、B、C mvn spring-boot:run -pl service-a mvn spring-boot:run -pl service-b mvn spring-boot:run -pl service-c
3 测试接口
# 测试Feign调用链路 curl http://localhost:8081/api/a/feign/trace # 测试RestTemplate调用链路 curl http://localhost:8081/api/a/rest/trace
4 查看追踪结果
访问Zipkin UI:http://localhost:9411
完整链路追踪效果
当调用成功后,你将在Zipkin UI中看到:
- 服务A到服务B的完整调用链
- 每个服务的执行时间
- Trace ID和Span ID
- 调用依赖关系图
控制台输出示例:
2024-01-01 10:00:00:123 [http-nio-8081-exec-1] INFO Service A - Feign调用开始
2024-01-01 10:00:00:124 [http-nio-8081-exec-1] INFO Service A - 生成TraceId: abc123, SpanId: def456
2024-01-01 10:00:00:126 [http-nio-8081-exec-1] INFO Service B - 收到请求
2024-01-01 10:00:00:127 [http-nio-8081-exec-1] INFO Service B - TraceId: abc123, SpanId: ghi789
2024-01-01 10:00:00:500 [http-nio-8081-exec-1] INFO Service C - 开始处理请求
2024-01-01 10:00:00:1000[http-nio-8081-exec-1] INFO Service C - 处理完成
这个案例展示了完整的Spring Cloud Sleuth链路追踪实现,包括:
- 服务间调用追踪
- 自定义Span
- 异步任务追踪
- Zipkin集成
- 日志关联
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播