SpringCloud--Sleuth 分布式链路追踪

一、Sleuth 概述

Sleuth 是 Spring Cloud 生态系统中的一个组件,它提供了分布式追踪的解决方案。在微服务架构中,一个请求可能会经过多个服务,Spring Cloud Sleuth 可以帮助我们跟踪这些请求,为每个请求生成一个唯一的追踪ID,并在日志中记录这些ID,从而使得我们可以轻松地追踪请求在各个微服务中的流转情况。

二、Sleuth 核心概念

  1. Trace:一次完整的分布式请求跟踪,相当于一个树状结构,从请求到第一个服务开始,到最后一个服务返回响应结束。每个 Trace 有一个唯一的 Trace ID。
  2. Span:表示一个基本的工作单元,比如一次 HTTP 请求、一次数据库调用等。每个 Span 有一个唯一的 Span ID。一个 Trace 由多个 Span 组成,这些 Span 之间具有父子关系。
  3. Annotation:用于记录事件的时间点,例如:
    • cs(Client Sent):客户端发送请求,表示开始一个 Span。
    • sr(Server Received):服务端接收请求,并开始处理。
    • ss(Server Sent):服务端发送响应,表示处理完成。
    • cr(Client Received):客户端接收响应,表示 Span 结束。

三、Sleuth 核心功能

  1. 分布式追踪:跟踪请求在微服务间的流转路径。
  2. Span 管理:记录每个服务处理单元的工作单元。
  3. Trace 上下文传播:在服务间传递追踪上下文。
  4. 与 Zipkin 集成:将追踪数据导出到 Zipkin 进行可视化分析。

四、Sleuth 实现原理

Spring Cloud Sleuth 的实现原理可以总结为:

  1. 标识与上下文: 通过 TraceId 和 SpanId 唯一标识请求链路和单元。
  2. 传播与透传: 利用 ThreadLocal 进行进程内上下文存储,并通过 HTTP 头等载体进行跨服务上下文传播。
  3. 自动化与透明: 通过自动仪器化主流框架组件,对开发者透明地完成追踪逻辑的植入。
  4. 收集与可视化: 通过 Reporter 将完成的 Span 数据上报给 Zipkin 等后端,进行聚合、存储和图形化展示。
  5. 日志增强: 通过与 MDC 集成,无缝地将追踪信息输出到应用日志中,极大方便了问题排查。

五、实现案例(Sleuth + Zipkin)

使用Spring Boot和Spring Cloud Sleuth与Zipkin来实现分布式链路追踪,主要步骤:

  1. 创建两个Spring Boot服务:一个作为服务提供者(provider),一个作为服务消费者(consumer),消费者调用提供者的服务。
  2. 引入Spring Cloud Sleuth和Zipkin的依赖。
  3. 配置Zipkin服务器地址。
  4. 通过RestTemplate或FeignClient进行服务调用,Sleuth会自动追踪。
  5. 启动Zipkin服务器来收集和展示追踪数据。
5.1 环境准备

使用 Docker 启动 Zipkin Server。

bash 复制代码
# 使用 Docker 运行 Zipkin
docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin

# 或者使用命令行启动(需要 Java 环境)
# curl -sSL https://zipkin.io/quickstart.sh | bash -s
# java -jar zipkin.jar
5.2 添加依赖

在服务提供者和服务消费者服务的pom.xml文件中分别添加Sleuth和Zipkin的依赖。

xml 复制代码
<!-- Spring Cloud 版本管理 -->
<properties>
    <spring-cloud.version>2022.0.4</spring-cloud.version>
</properties>

<!-- Sleuth + Zipkin 依赖 -->
<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>
5.3 配置文件

在服务提供者和服务消费者服务的application.yml配置文件中配置Zipkin服务器地址。

yaml 复制代码
spring:
  application:
    name: user-service
  sleuth:
    sampler:
      probability: 1.0  # 采样率,1.0表示100%采样
  zipkin:
    base-url: http://localhost:9411  # Zipkin服务器地址
    sender:
      type: web
5.4 服务间调用追踪

当服务都启动成功后,进行服务间的调用时,Sleuth会自动在header中添加Trace信息。

java 复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    
    private final RestTemplate restTemplate;
    private final UserService userService;
    
    public UserController(RestTemplate restTemplate, UserService userService) {
        this.restTemplate = restTemplate;
        this.userService = userService;
    }
    
    @GetMapping("/{userId}/orders")
    public UserOrderInfo getUserOrders(@PathVariable Long userId) {
        // Sleuth会自动在header中添加Trace信息
        ResponseEntity<OrderList> orderResponse = restTemplate.exchange(
            "http://order-service/orders/user/" + userId,
            HttpMethod.GET,
            null,
            OrderList.class
        );
        
        User user = userService.getUserById(userId);
        return new UserOrderInfo(user, orderResponse.getBody());
    }
}
5.5 查看 Zipkin 链路追踪

访问 http://localhost:9411 查看链路追踪数据:

  1. 在 Zipkin UI 中可以看到服务间的调用关系。
  2. 查看每个请求的详细链路信息。
  3. 分析服务调用的耗时和依赖关系。

六、高级配置和自定义

6.1 自定义 Span 标签
java 复制代码
@Service
public class UserService {
    
    private final Tracer tracer;
    
    public UserService(Tracer tracer) {
        this.tracer = tracer;
    }
    
    public User getUserById(Long userId) {
        // 创建自定义Span
        Span userSpan = tracer.nextSpan().name("db.user.query").start();
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(userSpan)) {
            // 业务逻辑
            userSpan.tag("user.id", userId.toString());
            userSpan.event("query.user.from.database");
            
            // 模拟数据库查询
            return userRepository.findById(userId);
        } catch (Exception e) {
            userSpan.error(e);
            throw e;
        } finally {
            userSpan.end();
        }
    }
}
6.2 异步方法追踪
java 复制代码
@Async
@Sleuth
public CompletableFuture<User> getUserAsync(Long userId) {
    // Sleuth会自动传递Trace上下文
    return CompletableFuture.supplyAsync(() -> userRepository.findById(userId));
}
6.3 自定义采样策略
java 复制代码
@Configuration
public class TraceConfig {
    
    @Bean
    public Sampler defaultSampler() {
        // 自定义采样策略:只追踪重要接口
        return new Sampler() {
            @Override
            public boolean isSampled(TraceContext traceContext) {
                // 根据请求路径决定是否采样
                String path = getCurrentRequestPath();
                return path.startsWith("/api/") || path.startsWith("/internal/");
            }
        };
    }
}
6.4 数据库调用追踪
java 复制代码
@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        // 使用P6Spy拦截SQL语句
        P6SpyDataSource dataSource = new P6SpyDataSource(actualDataSource());
        return dataSource;
    }
}
6.5 消息队列追踪
java 复制代码
@Component
public class MessageService {
    
    private final Tracer tracer;
    private final StreamBridge streamBridge;
    
    public void sendUserCreatedEvent(User user) {
        // 手动注入Trace信息到消息头
        Message<User> message = MessageBuilder.withPayload(user)
            .setHeader("traceId", tracer.currentSpan().context().traceId())
            .setHeader("spanId", tracer.currentSpan().context().spanId())
            .build();
            
        streamBridge.send("user-created-out-0", message);
    }
}
相关推荐
IT_陈寒36 分钟前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码2 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo2 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志3 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..3 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects3 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰3 小时前
一些基础和问题解决
后端
陈随易3 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN3 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp
Imchendiana3 小时前
《狂人日记NO.9》— 前后端一把梭,我的全栈实录
前端·后端