如何在 Spring Boot3 中整合 Jaeger 实现分布式链路追踪?

如何在 Spring Boot3 中整合 Jaeger 实现分布式链路追踪?

解决方案goocz2025-04-27 14:44:366A+A-

各位互联网大厂的后端开发朋友们,你们在日常开发中,有没有遇到过排查分布式系统故障时的那种棘手情况呢?当一个请求在多个微服务之间穿梭,出了问题却很难快速定位到底是哪个环节出现了故障,是不是特别头疼?别担心,今天就来和大家聊聊一个超实用的解决方案 —— 在 Spring Boot3 中整合 Jaeger 实现分布式链路追踪。

背景介绍

在如今的分布式系统架构中,一个业务请求往往会涉及多个微服务的协同工作。以电商场景为例,一个简单的下单操作,从用户发起请求,可能就需要依次经过用户服务确认身份、商品服务检查库存、订单服务生成订单、支付服务完成交易等多个环节。这种复杂的架构虽然带来了灵活性和可扩展性,但也导致了一旦出现性能问题或者错误,想要准确找出问题所在变得异常困难。传统的日志记录方式在这种复杂的分布式环境下显得力不从心,因为各个服务产生的日志分散存储,难以关联起来形成完整的请求链路,就如同散落一地的拼图碎片,很难拼凑出全貌。

分布式链路追踪技术的出现,正是为了解决这个难题。它就像一个精准的导航仪,能够记录一个请求从发起开始,在各个微服务之间传递的完整路径和每个环节的耗时等信息,让我们对整个请求的流转过程一目了然。在众多分布式链路追踪系统中,Jaeger 凭借其开源、高性能、可扩展等特性,受到了广泛的应用。而 Spring Boot3 作为当下热门的 Java 开发框架,以其强大的自动配置和便捷的依赖管理等优势,为我们整合 Jaeger 提供了诸多便利。

Jaeger 工作原理简介

Jaeger 遵循 OpenTracing 标准,在这个标准中,一个工作的逻辑单元被称为 span。每个 span 有自己的名称、开始时间、时长、标签等特性。简单来说,当一个请求进入分布式系统,就会生成一个唯一的 Trace ID,这个 Trace ID 会在整个请求链路中传递。而每个 span 则代表了 Trace 中的一个具体操作,比如某个微服务中的方法调用。通过这些 span 的有序组合,就可以完整还原出请求的调用链路。从架构上看,应用使用了 Jaeger 的 SDK 后,业务的 span 会被送到 jaeger - agent(通过 UDP 协议),然后 jaeger - agent 将其推送到 jaeger - collector,数据被 jaeger - collector 写入数据库(DB)。最后,用户通过浏览器访问 jaeger - query,就能看到详细的调用链追踪结果 。

具体实现

添加依赖

首先,我们需要在项目的pom.xml文件中添加 Jaeger 的依赖。在<dependencies>标签内加入以下代码:

<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
    <version>3.3.1</version>
</dependency>

这个依赖会引入 Jaeger 相关的库,让我们的 Spring Boot3 项目能够与 Jaeger 进行交互。它如同搭建桥梁的基石,为后续的配置和使用奠定基础。通过引入这个依赖,项目中就具备了与 Jaeger 通信、生成和管理链路追踪数据的能力。

配置文件

接着,在application.yml配置文件中进行 Jaeger 的相关配置。在文件中添加如下内容:

opentracing:
  jaeger:
    udp - sender:
      host: localhost
      port: 6831
    log - spans: true

这里配置了 Jaeger 的 UDP 发送器的主机和端口,默认情况下,如果你的 Jaeger 服务器运行在本地,就可以使用上述配置。log - spans: true表示开启日志记录跨度信息,方便我们后续排查问题。UDP 发送器负责将应用产生的 span 数据发送给 jaeger - agent。而开启日志记录跨度信息,就像是给每个 span 操作留下脚印,当出现问题时,我们可以顺着这些脚印去查找问题根源。

代码实现

在代码层面,Spring Boot3 会自动配置 Jaeger 的相关组件。我们只需要确保我们的各个微服务在进行请求交互时,遵循统一的链路追踪规范即可。例如,在服务之间传递 HTTP 请求时,Spring Cloud Sleuth 会自动为每个请求生成唯一的追踪 ID(Trace ID)和跨度 ID(Span ID),并将这些信息附加在 HTTP 请求头中进行传递。这样,Jaeger 就能收集到完整的链路信息。

假设我们有两个微服务,一个是用户服务(UserService),另一个是商品服务(ProductService)。在用户服务中,我们有一个获取用户信息并调用商品服务获取推荐商品的接口。

首先是用户服务的 Controller 代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import brave.Span;
import brave.Tracer;

@RestController
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private Tracer tracer;

    @GetMapping("/user/{userId}/recommended-products")
    public String getRecommendedProducts(@PathVariable String userId) {
        // 创建一个新的span,用于记录获取推荐商品的操作
        Span span = tracer.nextSpan().name("getRecommendedProducts");
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span.start())) {
            // 模拟获取用户信息的操作
            String userInfo = "User " + userId + " information";
            span.tag("userInfo", userInfo);

            // 调用商品服务获取推荐商品
            String productServiceUrl = "http://product-service/products/recommended?userId=" + userId;
            String recommendedProducts = restTemplate.getForObject(productServiceUrl, String.class);
            span.tag("recommendedProducts", recommendedProducts);

            return "User: " + userInfo + ", Recommended Products: " + recommendedProducts;
        } finally {
            span.finish();
        }
    }
}

在商品服务的 Controller 中,处理推荐商品请求的代码如下:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import brave.Span;
import brave.Tracer;

@RestController
public class ProductController {

    @Autowired
    private Tracer tracer;

    @GetMapping("/products/recommended")
    public String getRecommendedProducts(@RequestParam String userId) {
        // 创建一个新的span,用于记录处理推荐商品请求的操作
        Span span = tracer.nextSpan().name("processRecommendedProductRequest");
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span.start())) {
            // 模拟获取推荐商品的逻辑
            String recommendedProducts = "Product 1, Product 2 for User " + userId;
            span.tag("recommendedProducts", recommendedProducts);

            return recommendedProducts;
        } finally {
            span.finish();
        }
    }
}

在上述代码中,通过 Spring Cloud Sleuth 与 Jaeger 的集成,每个微服务中的方法调用都被记录为一个 span。在用户服务中,获取用户信息和调用商品服务的操作分别被记录为不同的 span,并通过 Trace ID 关联在一起。商品服务中处理推荐商品请求的操作也被记录为 span。当出现问题时,我们可以通过 Trace ID 在 Jaeger 的 Web 界面中快速定位到整个请求链路中涉及的所有 span,从而全面分析问题。

查看追踪数据

当我们完成上述配置并启动项目后,访问http://localhost:16686(前提是你的 Jaeger 服务器正常运行在本地),就可以在 Jaeger 的 Web 界面中看到各个请求的链路追踪数据。在这里,我们可以清晰地看到每个 Span 的耗时、依赖关系等详细信息,从而快速定位到性能瓶颈或者错误发生的位置。

在 Jaeger 的 Web 界面中,展示的信息非常直观。每个 Trace 会以可视化的方式呈现出各个 span 的顺序和依赖关系,就像一张清晰的地图,展示了请求的行进路线。通过点击每个 span,我们可以查看其具体的开始时间、结束时间、耗时、标签等信息。如果某个 span 的耗时明显过长,很可能就是性能瓶颈所在;如果某个 span 出现了错误状态码,那就可以重点排查该 span 对应的服务或方法。例如,我们发现某个订单创建请求的 Trace 中,调用支付服务的 span 耗时达到了数秒,远远超过正常范围,那么就可以针对支付服务进行深入排查,看是否是网络延迟、代码逻辑问题还是资源不足导致的性能下降。

总结

通过在 Spring Boot3 中整合 Jaeger 实现分布式链路追踪,我们能够极大地提升排查分布式系统故障的效率,让我们的开发和运维工作更加轻松。各位后端开发的小伙伴们,不妨赶紧在自己的项目中尝试一下吧!如果你在实践过程中有任何问题或者心得,欢迎在评论区留言分享,让我们一起交流进步。

点击这里复制本文地址 以上内容由goocz整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

果子教程网 © All Rights Reserved.  蜀ICP备2024111239号-5