SpringCloud集成sleuth和zipkin实现微服务链路追踪

文章目录

前言

最近项目上准备引入接口调用链路追踪,说到这个我们就不得不想到springcloud全家桶中的sleuth了。他可以将跨多个服务请求链路记录下来,供我们查询分析。然后,我们在此基础上用zipkin来采集和上报分析请求链路,简直不要太爽。那么今天就分享一期微服务架构接入sleuth+zipkin实战演示。

技术积累

spring cloud sleuth介绍

Sleuth是Spring cloud的分布式跟踪解决方案。

1.span(跨度),基本工作单元。一次链路调用,创建一个span,span用一个64位id唯一标识。包括:id,描述,时间戳事件,spanld,span父id.span被启动和停止时,记录了时间信息,初始化span叫:rootspan,它的span id和trace id相等。

2.trace(跟踪),一组共享"root span"的span组成的树状结构称为 trace,trace也有一个64位ID,trace中所有span共享一个trace id。类似于一颗 span 树。

3.annotation (标签),annotation用来记录事件的存在,其中,核心annotation用来定义请求的开始和结束CS(Client Send客户端发起请求)。客户端发起请求描述了span开始,

SR(Server Received服务端接到请求)。服务端获得请求并准备处理它。SR-CS=网络延迟SS(Server Send服务器端处理完成,并将结果发送给客户端)。表示服务器完成请求处理,响应客户端时。SS-SR=服务器处理请求的时间,

CR(Client Received 客户端接受服务端信息)。span结束的标识。客户端接收到服务器的响应。CR-CS=客户端发出请求到服务器响应的总时间。

其实数据结构是一颗树,从root span 开始。

工具定位

Spring Cloud Sleuth是一个用于Spring Cloud应用程序的分布式追踪工具,它主要专注于在服务间传播追踪信息。

主要功能

传播追踪上下文:在服务调用间传递Trace ID和Span ID,确保整个请求链路的追踪信息保持一致。

集成日志框架:修改日志框架的配置,使得日志记录中包含Trace和Span的ID。

与Spring Cloud生态集成:与Spring Cloud的其他组件(如Ribbon、Hystrix、Zuul等)无缝集成。

依赖性:Sleuth是为Spring Cloud应用程序设计的,它依赖于Spring框架和Spring Boot。

zipkin介绍

Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系统的实时监控数据。

工具定位

Zipkin是一个分布式追踪系统,它提供了追踪数据的收集、存储、查找和展示功能。

主要功能

收集追踪数据:Zipkin通过其客户端库(如Brave)收集追踪数据。

存储追踪数据:支持多种存储方案,如内存、MySQL、Cassandra、Elasticsearch等。

查询和展示:提供Web界面,用于查询追踪数据,并以可视化的方式展示服务间的调用关系和延迟。

独立性:Zipkin可以独立于任何应用程序或框架运行,并且可以与多种编程语言和框架集成。

zipkin 官网

https://zipkin.io/pages/quickstart.html

Zipkin与Sleuth的协作

数据收集:Sleuth负责在Spring Cloud应用程序中生成和传播追踪数据,而Zipkin负责收集这些数据。

数据展示:Sleuth生成的追踪数据可以通过Zipkin的Web界面进行查询和展示。

集成:在Spring Cloud应用程序中,Sleuth通常与Zipkin一起使用,Sleuth负责追踪信息的生成和传播,Zipkin负责存储和展示。

SpringCloud多模块搭建

多模块微服务项目结构

新增父项目来管理微服务各个模块

#父模块pom必须声明

pom

新增子模块

Zipkin Server部署

由于springcloud F版本不支持自定义zipkin server,故我们采用docker镜像进行部署zipkin server。

docker pull 镜像

docker pull openzipkin/zipkin:latest

启动zipkin server

未持久化(内存):

docker run --name zipkin-server -d --restart=always -p 9411:9411 openzipkin/zipkin:latest

http://127.0.0.1:9411/zipkin/

持久化到MySQL5:

docker run --name zipkin-server-mysql -d --restart=always -p 19411:9411 -e MYSQL_USER=root -e MYSQL_PASS=12345678 -e MYSQL_HOST=127.0.0.1 -e STORAGE_TYPE=mysql -e MYSQL_DB=zipkin -e MYSQL_TCP_PORT=13306 openzipkin/zipkin:3.3

mysql DD语句如下:

复制代码
https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql
--
-- Copyright The OpenZipkin Authors
-- SPDX-License-Identifier: Apache-2.0
--

CREATE TABLE IF NOT EXISTS zipkin_spans (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  `trace_id` BIGINT NOT NULL,
  `id` BIGINT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `remote_service_name` VARCHAR(255),
  `parent_id` BIGINT,
  `debug` BIT(1),
  `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
  `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query',
  PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';

CREATE TABLE IF NOT EXISTS zipkin_annotations (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
  `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
  `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
  `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
  `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
  `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
  `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
  `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
  `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
  `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
  `day` DATE NOT NULL,
  `parent` VARCHAR(255) NOT NULL,
  `child` VARCHAR(255) NOT NULL,
  `call_count` BIGINT,
  `error_count` BIGINT,
  PRIMARY KEY (`day`, `parent`, `child`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

持久化到ES:

docker run --name zipkin-server-es -d -p 19411:9411 --restart=always -e STORAGE_TYPE=elasticsearch -e ES_HOSTS=127.0.0.1:9200 openzipkin/zipkin:latest

JAR运行:

curl -sSL https://zipkin.io/quickstart.sh | bash -s

java -jar zipkin.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=12345678

我们测试选择不持久化

docker run -d -p 9411:9411 --name zipkinServer --restart=always openzipkin/zipkin

3、启动完成

SpringCloud 接入 Sleuth 与 Zipkin

pom引入依赖 (springboot2.6+)

复制代码
<properties>
    <spring-cloud.version>2021.0.5</spring-cloud.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-sleuth</artifactId>
        <version>3.1.10</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        <version>3.1.10</version>
    </dependency>
</dependencies>

appilication.yml配置修改

复制代码
spring:
  zipkin:
    #zipkin服务所在地址
    base-url: http://127.0.0.1:9411/
    sender:
      type: web #使用http的方式传输数据
    #配置采样百分比
    sleuth:
      sampler:
        probability: 1 # 将采样比例设置为 1.0,也就是全部都需要。默认是0.1也就是10%,一般情况下,10%就够用了
##打开debug日志
logging:
  level:
    org.springframework.web.servlet.DispatcherServlet: Debug

type=web也就是通过 HTTP 的方式发送数据到 Zipkin 。

如果请求量比较大,这种方式其实性能是比较低的,一般情况下我们都是通过消息中间件来发送,比如 RabbitMQ 。如果日志数据量比较大,一般推荐拥有更高吞吐量的 Kafka 来进行日志推送。这种方式就是让服务将 Sleuth 收集的日志推给 MQ ,让 Zipkin 去监控 MQ 的信息,通过 MQ 的队列获取到服务的信息。这样就提高了性能。

而日志的存储则可以采用mysql、 Elasticsearch 对数据进行持久化,这样可以保证 Zipkin 重启后,链路信息不会丢失。

增加测试链路代码

链路为:

user-service --> order-service --> finance-service

user-service增加请求入口getOrderAmount:

复制代码
/**
 * 计算订单金额
 * @param orderId
 * @author senfel
 * @date 2024/12/9 18:25
 * @return java.lang.String
 */
@RequestMapping("/getOrderAmount")
public String getOrderAmount(@RequestParam(name = "orderId") String orderId) {
    return orderService.getOrderAmount(orderId);
}

user-service增加feign调用order-service:

复制代码
/**
 * FeeService
 * @author senfel
 * @version 1.0
 * @date 2024/12/9 18:02
 */
@FeignClient(name = "order-service",fallback = OrderServiceFallback.class)
public interface getOrderAmount {

    /**
     * 获取订单金额
     * @param orderId
     * @author senfel
     * @date 2024/12/9 18:06
     * @return java.lang.String
     */
    @RequestMapping(value = "/order/getOrderAmount",method = RequestMethod.GET)
    String getOrderAmount(@RequestParam(name = "orderId") String orderId);
}

order-service增加调用入口getOrderAmount:

复制代码
/**
 * 获取订单金额
 * @param orderId
 * @author senfel
 * @date 2024/12/9 18:06
 * @return java.lang.String
 */
@RequestMapping(value = "/getOrderAmount",method = RequestMethod.GET)
public String getOrderAmount(@RequestParam(name = "orderId") String orderId){
    return financeService.getFee(orderId);
}

order-service增加feign调用finance-service:

复制代码
/**
 * FinanceService
 * @author senfel
 * @version 1.0
 * @date 2024/12/9 18:10
 */
@FeignClient(name = "finance-service",fallback = FinanceServiceFallback.class)
public interface FinanceService {

    /**
     * 获取费用
     * @param orderId
     * @author senfel
     * @date 2024/12/9 18:12
     * @return java.lang.String
     */
    @RequestMapping(value = "/finance/getFee",method = RequestMethod.GET)
    String getFee(@RequestParam(name = "orderId") String orderId);
}

finance-service增加请求入口getFee:

复制代码
/**
 * 获取费用
 * @param orderId
 * @author senfel
 * @date 2024/12/9 18:12
 * @return java.lang.String
 */
@RequestMapping(value = "/getFee",method = RequestMethod.GET)
public String getFee(@RequestParam(name = "orderId") String orderId) {
    return "100";
}

调用微服务进行验证

启动微服务调用getOrderAmount()

进入zipkin界面查看请求调用链路和各个阶段耗时


至此,我们已经完微服务链路追踪实战.。

相关推荐
whisperrr.几秒前
【spring01】Spring 管理 Bean-IOC,基于 XML 配置 bean
xml·java·spring
天上掉下来个程小白10 分钟前
HttpClient-03.入门案例-发送POST方式请求
java·spring·httpclient·苍穹外卖
robin_suli1 小时前
Spring事务的传播机制
android·java·spring
喵个咪1 小时前
开箱即用的GO后台管理系统 Kratos Admin - 定时任务
后端·微服务·消息队列
暮乘白帝过重山2 小时前
Singleton和Prototype的作用域与饿汉式/懒汉式的初始化方式
spring·原型模式·prototype·饿汉式·singleton·懒汉式
ejinxian3 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之3 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码3 小时前
Spring Task 定时任务
java·前端·spring
爱的叹息3 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
松韬4 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存