JAVA 微服务间调用链过长?分布式追踪 Sleuth + Zipkin 实战教程

好的,下面我们开始今天的讲座,主题是Java微服务间调用链过长?分布式追踪 Sleuth + Zipkin 实战教程。

开场白:微服务架构的挑战

各位同学,大家好!很高兴今天能在这里和大家一起探讨微服务架构下的一个重要议题:分布式追踪。随着微服务架构的日益普及,我们也面临着新的挑战。其中,服务间调用链过长,导致问题定位困难就是一个很突出的问题。想象一下,一个用户请求可能需要经过数十个微服务才能完成,任何一个环节出现问题都可能导致整个请求失败。没有有效的追踪手段,我们就如同在黑暗中摸索,难以快速定位和解决问题。

今天,我们将重点介绍如何使用Spring Cloud Sleuth和Zipkin来实现分布式追踪,帮助大家有效地监控和诊断微服务架构中的性能瓶颈和错误。

第一部分:分布式追踪的必要性与基本概念

在传统的单体应用中,我们可以通过日志、调试器等工具轻松追踪请求的执行路径。但在微服务架构中,请求跨越多个服务,传统的追踪方法就显得力不从心。分布式追踪系统应运而生,它能够记录请求在不同服务间的调用关系,并提供可视化的界面,帮助我们理解请求的完整生命周期。

  • Trace(跟踪): 代表一个完整的请求链路,例如用户发起一个HTTP请求,经过多个微服务的处理,最终返回响应。一个Trace由多个Span组成。
  • Span(跨度): 代表Trace中的一个基本单元,通常对应于一个服务中的一个操作,例如一个HTTP请求、一个数据库查询等。Span记录了操作的开始时间、结束时间、以及相关元数据(如服务名、方法名、请求参数等)。
  • Span Context(跨度上下文): 包含了Span的唯一标识(Trace ID 和 Span ID)以及其他需要在服务间传递的信息,用于关联不同服务中的Span。
  • Annotation(注解): 用于记录Span在不同阶段发生的事件,例如cs(Client Send,客户端发送请求)、sr(Server Receive,服务端接收请求)、ss(Server Send,服务端发送响应)、cr(Client Receive,客户端接收响应)。

第二部分:Spring Cloud Sleuth 简介

Spring Cloud Sleuth是一个Spring Cloud组件,它为Spring Boot应用提供了分布式追踪的能力。Sleuth通过拦截器和过滤器自动为请求添加Trace ID和Span ID,并将这些信息传递给下游服务。它还负责将追踪数据发送到指定的收集器(例如Zipkin)。

Sleuth的主要特点:

  • 自动集成: Sleuth可以与Spring Boot应用无缝集成,无需修改大量的代码。
  • 支持多种传输方式: Sleuth支持将追踪数据通过HTTP、Kafka、RabbitMQ等多种方式发送到收集器。
  • 灵活的配置: Sleuth提供了丰富的配置选项,可以根据实际需求进行定制。
  • 兼容性: Sleuth可以与多种分布式追踪系统集成,例如Zipkin、Jaeger等。

第三部分:Zipkin 简介

Zipkin是一个开源的分布式追踪系统,用于收集、存储和展示Sleuth生成的追踪数据。Zipkin提供了一个Web界面,可以查询和分析Trace,帮助我们快速定位性能瓶颈和错误。

Zipkin的主要组件:

  • Collector(收集器): 接收来自各个服务的追踪数据,并将数据存储到存储介质中。
  • Storage(存储): 用于存储追踪数据,Zipkin支持多种存储介质,例如内存、MySQL、Elasticsearch、Cassandra等。
  • API(接口): 提供查询和检索追踪数据的接口。
  • Web UI(Web界面): 提供可视化的界面,用于查询和分析Trace。

第四部分:实战演练:Sleuth + Zipkin 集成

接下来,我们将通过一个简单的示例来演示如何使用Spring Cloud Sleuth和Zipkin来实现分布式追踪。

4.1 环境准备

  • JDK 8 或以上
  • Maven
  • Docker (可选,用于运行Zipkin)
  • 一个简单的 Spring Boot 微服务项目 (如果没有,可以创建一个)

4.2 启动 Zipkin

最简单的方式是使用Docker启动Zipkin:

docker run -d -p 9411:9411 openzipkin/zipkin

启动成功后,可以通过浏览器访问 http://localhost:9411 查看 Zipkin 的 Web 界面。

4.3 创建 Spring Boot 微服务项目

创建一个名为 trace-service 的 Spring Boot 项目。

4.3.1 添加依赖

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.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </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>

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <!-- 根据实际情况选择合适的版本 -->
</properties>

注意: spring-cloud.version 需要根据实际情况选择合适的版本。 这里仅作为一个例子.

4.3.2 创建 Controller

创建一个简单的 Controller,用于模拟服务间的调用。

package com.example.traceservice.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class TraceController {

    private static final Logger logger = LoggerFactory.getLogger(TraceController.class);

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/trace")
    public String trace() {
        logger.info("=====<call trace-service>=====");
        return "Trace Service";
    }

    @GetMapping("/trace2")
    public String trace2() {
        logger.info("=====<call trace-service2>=====");
        String result = restTemplate.getForObject("http://localhost:8081/trace", String.class); // 假设有另一个服务运行在 8081 端口
        logger.info("=====<call trace-service2, return>=====");
        return "Trace Service 2, " + result;
    }

    //  配置RestTemplate (如果还没有配置)
    //  在主类或者配置类中添加
    //  @Bean
    //  public RestTemplate restTemplate(){
    //      return new RestTemplate();
    //  }
}

4.3.3 配置 application.properties

src/main/resources/application.properties 文件中添加以下配置:

spring.application.name=trace-service
server.port=8080
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.probability=1.0  # 采样率,1.0 表示全部采样
#logging.level.org.springframework.web=DEBUG # 开启详细日志,便于调试

解释:

  • spring.application.name: 指定应用的名称,用于在Zipkin中标识服务。
  • server.port: 指定应用的端口。
  • spring.zipkin.base-url: 指定Zipkin服务器的地址。
  • spring.sleuth.sampler.probability: 指定采样率,1.0表示全部请求都进行追踪。在生产环境中,可以适当降低采样率,以减少性能开销。

4.4 创建另一个 Spring Boot 微服务项目(trace-service2)

创建一个名为 trace-service2 的 Spring Boot 项目,端口设置为 8081,代码类似 trace-service,仅仅修改 Controller 的返回值和端口。

4.4.1 添加依赖

pom.xml文件与trace-service相同。

4.4.2 创建 Controller

package com.example.traceservice2.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TraceController {

    private static final Logger logger = LoggerFactory.getLogger(TraceController.class);

    @GetMapping("/trace")
    public String trace() {
        logger.info("=====<call trace-service2>=====");
        return "Trace Service 2";
    }
}

4.4.3 配置 application.properties

spring.application.name=trace-service2
server.port=8081
spring.zipkin.base-url=http://localhost:9411
spring.sleuth.sampler.probability=1.0

4.5 运行项目

分别运行 trace-servicetrace-service2 两个项目。

4.6 访问接口

访问 http://localhost:8080/trace2,该接口会调用 http://localhost:8081/trace

4.7 查看 Zipkin 界面

打开 Zipkin 的 Web 界面 http://localhost:9411,可以看到追踪数据。点击 "Find a trace",就可以看到完整的调用链。

第五部分:Sleuth + Zipkin 的高级用法

5.1 自定义 Span

除了 Sleuth 自动创建的 Span,我们还可以手动创建 Span,用于更精确地追踪代码的执行过程。

import brave.Span;
import brave.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Autowired
    private Tracer tracer;

    public void doSomething() {
        Span span = tracer.nextSpan().name("my-custom-span").start();
        try {
            // 执行一些业务逻辑
            Thread.sleep(100); // 模拟耗时操作
        } finally {
            span.finish();
        }
    }
}

解释:

  • tracer.nextSpan().name("my-custom-span").start():创建一个新的 Span,并设置 Span 的名称为 "my-custom-span"。
  • span.finish():结束 Span。

5.2 自定义 Tag

我们可以为 Span 添加自定义的 Tag,用于记录一些重要的信息。

import brave.Span;
import brave.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Autowired
    private Tracer tracer;

    public void doSomething(String param) {
        Span span = tracer.nextSpan().name("my-custom-span").start();
        try {
            // 执行一些业务逻辑
            span.tag("my-param", param); // 添加自定义 Tag
            Thread.sleep(100); // 模拟耗时操作
        } finally {
            span.finish();
        }
    }
}

解释:

  • span.tag("my-param", param):为 Span 添加一个名为 "my-param" 的 Tag,值为 param

5.3 使用 Baggage

Baggage 是一种在服务间传递上下文信息的方式,例如用户ID、请求ID等。 Sleuth 支持 Baggage,可以将 Baggage 信息传递给下游服务。

(这部分代码比较复杂,需要涉及到 BaggageField 的定义和传播,这里先省略,如果大家感兴趣,可以自行查阅相关资料。)

第六部分:常见问题及解决方案

  • Zipkin 界面无法显示追踪数据:

    • 检查 spring.zipkin.base-url 配置是否正确。
    • 检查服务是否成功启动,并且能够正常访问 Zipkin 服务器。
    • 检查采样率是否设置过低。
    • 检查日志,查看是否有 Sleuth 或 Zipkin 相关的错误信息。
  • 调用链不完整:

    • 确保所有参与调用的服务都集成了 Sleuth。
    • 检查服务间的调用方式是否支持 Sleuth 的自动传播(例如,使用 RestTemplate 或 FeignClient)。
    • 检查是否有中间件或代理服务器拦截了请求,导致 Trace ID 丢失。
  • 性能问题:

    • 适当降低采样率。
    • 选择合适的存储介质(例如,使用 Elasticsearch 代替内存)。
    • 优化 Zipkin 的配置。

第七部分:表格总结常用配置

配置项 描述 默认值
spring.application.name 应用名称,用于在 Zipkin 中标识服务。
server.port 应用端口。 8080
spring.zipkin.base-url Zipkin 服务器的地址。 http://localhost:9411
spring.sleuth.sampler.probability 采样率,取值范围为 0.0 到 1.0,1.0 表示全部采样。 0.1
spring.sleuth.sampler.percentage 采样百分比,取值范围为 0 到 100,100 表示全部采样。(spring.sleuth.sampler.probability 优先级高于 spring.sleuth.sampler.percentage
spring.zipkin.enabled 是否启用 Zipkin。 true
spring.sleuth.enabled 是否启用 Sleuth。 true
spring.zipkin.sender.type Zipkin 数据发送方式,可选值包括 webrabbitkafka web
spring.zipkin.locator.discovery.enabled 是否使用服务发现来定位 Zipkin 服务器。 false
spring.sleuth.baggage.enabled 是否启用 Baggage。 false
spring.sleuth.baggage.remote-fields 需要远程传播的 Baggage 字段列表,多个字段之间用逗号分隔。
logging.level.org.springframework.web Spring Web 的日志级别,可以设置为 DEBUG,以便查看 Sleuth 的详细日志。 INFO

结尾:掌握分布式追踪,提升微服务治理能力

通过今天的讲解和演示,相信大家对Spring Cloud Sleuth和Zipkin的集成使用有了更深入的了解。掌握分布式追踪技术,能够帮助我们更好地理解微服务架构的运行状况,快速定位和解决问题,提升微服务治理能力。希望大家在实际项目中积极应用这些技术,构建更加稳定和可靠的微服务应用。谢谢大家!

分布式追踪:问题定位的关键

Sleuth + Zipkin 的组合,有效追踪微服务间的调用链,助力快速定位问题和优化性能。掌握这些工具,让微服务架构更清晰可控。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注