Spring Cloud Sleuth 分布式链路追踪入门

您好,我是今夜写代码,今天学习下分布式链路组件Spring Cloud Sleuth

本文内容

  • 介绍了分布式链路的思想

  • Sleuth 和 Zipkin 简单集成Demo,并不涉及 Sleuth原理。

为什么要用链路追踪?

微服务架构下,一个复杂的电商应用,完成下单可能依赖商品、库存、结算、风控等一系列服务,并且依赖的服务又依赖一堆服务,其中任何一个环节出错都可能导致服务调用失败。 一旦服务调用失败,对于问题排查的成本是非常高的。

究竟哪个环节出错了?请求日志在哪一个服务节点上? 在没有调用链路追踪情况下,相信这绝对是一大难题。

早在2010年一篇由谷歌发表的 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure 》横空出世, 这篇论文详细介绍了谷歌的分布式系统追踪基础设施 Dapper, 当时这篇论文在国内应该是思想超前的,大学还在教JSP、Servlet。 如今链路追踪在大型分布式应用中,起到很重要的一席之地。

如何自己实现简单的链路追踪?

我们可以按照下面流程实现一个简单的链路追踪,当然不包含链路上报和检索功能。

以常见的 Dubbo 和 Spring MVC 请求举例,实现将请求Trace Id传递下去,并且满足如下特征

  • 对业务代码无入侵

  • 业务代码可以通过API方式操作Trace Id

图片

Dubbo

Dubbo attachment和过滤器等等不熟悉的同学可以查看之前文章Dubbo学习合集。

我们可以通过Dubbo的attachment 实现Trace Id 透传,实现对业务代码无入侵。 同时为了业务代码获取方便,我们将TraceId 放入ThreadLocal中,这样业务代码可以通过ThreadLocal获取,而不必依赖Dubbo 的 RpcContext 。

  String traceId  = RpcContext.getContext().getAttachment("traceId");
  if(traceId == null){
      traceId = UUID.randomUUID().toString().replace("-","");
      RpcContext.getContext().setAttachment("traceId",traceId);
  }
  TraceUtil.setTraceId(traceId);
/**
 * @ClassName TraceUtil
 * @Description
 * @Author codetonight 今夜写代码 https://blog.csdn.net/happycao123
 * @Date 2024/12/14 16:06
 */
public class TraceUtil {

    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static String getTraceId(){
        return threadLocal.get();
    }

    public static void setTraceId(String traceId){
        threadLocal.set(traceId);
    }

    public static void remove(){
        threadLocal.remove();
    }

}

Spring MVC

Spring MVC 有自己的拦截器,我们也可以直接使用 servlet-api 中Filter 。同样的,我们将Trace Id 放入 ThreadLocal ,这样后面业务就可以通过ThreadLocal 操作Trace Id。

/**
 * @ClassName TraceIdFilter
 * @Description
 * @Author codetonight 今夜写代码 https://blog.csdn.net/happycao123
 * @Date 2024/12/14 15:52
 */
public class TraceIdFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String traceId = request.getHeader("traceId");
        if(traceId == null) {
           traceId = UUID.randomUUID().toString().replace("-","");
        }
        TraceUtil.setTraceId(traceId);
        try{
            filterChain.doFilter(servletRequest,servletResponse);
        }finally {
            TraceUtil.remove();
        }
    }
}

上面实现比较粗糙,信息也不够全,还存在异步方法链路无法透传问题,当然更重要的缺乏链路查询的支持。

Spring Cloud Sleuth

Spring Cloud Sleuth 是一个分布式追踪的组件,我们无需再重复造轮子。

相关概念

  • Trace ID: 整个调用链全局唯一的Id,无论经过多少个服务,整个调用链中 TraceId都相同

  • Span ID: 标识一次具体的操作或服务调用,同样全局唯一。

  • Parent ID: 记录当前服务调用发起方的 Span Id。

一个复杂的微服务调用,可能出现服务的层层嵌套,通过Parent ID ,可以梳理整个调用链的上下游关系。

如图服务A 调用服务B ,服务B 又调用了服务C ,TraceId 对于整个调用链是不变的。 各种的服务都有自己的Span ID ,Parent ID记录了其直接调用方服务对应的Span ID 。

图片

Zipkin

Sleuth可以实现服务调用的链路透传, 如果需要实现链路检索功能,可以使用Zipkin,Zipkin核心功能日志收集和链路检索,Zipkin 具有可视化页面。

Zipkin 安装

如果您使用的是 Java 17或者更高版本,可以通过编译源码方式生成  zipkin-server-*exec.jar

java -jar ./zipkin-server/target/zipkin-server-*exec.jar
Docker 安装方式

docker 方式一个命令即可,启动成功输入 http://{ip}:9411/,即可进入Web 页面

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

图片

Spring Cloud Sleuth 简单Demo

Server 作为SpringBoot 启动类,同时提供了/hello http 接口。

Client 作为SpringBoot 启动类,提供/callServer接口,其内部通过restTemplate调用 Server/hello接口。

/**
 * @ClassName Server
 * @Description
 * @Author codetonight 今夜写代码 https://blog.csdn.net/happycao123
 * @Date 2024/12/15 19:00
 */
@EnableAutoConfiguration
@RestController
@Slf4j
public class Server {


    private  final Tracer tracer;

    public Server(Tracer tracer) {
        this.tracer = tracer;
    }

    @RequestMapping("/hello")
    public String hello() {
        log.info("Server hello is called ");
//        Span currentSpan = tracer.currentSpan();
//        if (currentSpan != null) {
//            currentSpan.tag("custom-tag", "value");
//            currentSpan.annotate("Custom Event");
//        }
        return new Date().toString();
    }

    public static void main(String[] args) {
        SpringApplication.run(Server.class,
                "--spring.application.name=server",
                "--server.port=9000"
        );
    }
}
/**
 * @ClassName Client
 * @Description
 * @Author codetonight 今夜写代码 https://blog.csdn.net/happycao123
 * @Date 2024/12/15 19:04
 */
@EnableAutoConfiguration
@RestController
@Slf4j
public class Client {

    @Autowired
    RestTemplate restTemplate;

    String backendBaseUrl = "http://localhost:9000";

    @RequestMapping("/callServer") public String callServer() {
        log.info("callServer {}",new Date());
        return restTemplate.getForObject(backendBaseUrl + "/hello", String.class);
    }

    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(Client.class,
                "--spring.application.name=client",
                "--server.port=8081"
        );
    }
}

logging.level.org.springframework.web=DEBUG
spring.sleuth.traceId128=true
spring.sleuth.sampler.probability=1.0
# Adds trace and span IDs to logs (when a trace is in progress)
logging.pattern.level=[%X{traceId}/%X{spanId}] %-5p [%t] %C{2} - %m%n


spring.application.name=sleuth-service


spring.zipkin.base-url=http://localhost:9411

效果图

zipkin 控制台可以看到相关日志,我们自己打印的日志并没有TraceId 相关信息

图片

图片

业务日志增加TraceId 信息

很自然想到的方式通过API方式获取Trace ID相关信息,但这种相对比繁琐,业务代码有一定入侵,其实我们可以配置logback-spring.xml 控制日志格式,配置后再看看效果,我们自己的业务日志也包含Trace ID 等信息了。

API 获取链路信息简单例子
        Span currentSpan = tracer.currentSpan();
        System.out.println(currentSpan.context().traceIdString());
        System.out.println(currentSpan.context().spanIdString());
logback-spring.xml
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - traceId=%X{traceId:-}, spanId=%X{spanId:-} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

图片

总结

本文主要介绍了分布式链路追踪主要思想,以及Spring Cloud Slueth + Zipkin集成实现分布式链路追踪,当然本文只是入门级教程,不涉及Slueth原理相关,文中给的例子仅仅只是Demo,实际项目中,启动类与Controller 类职责要严格区分,不能混在一起,更多Demo 可以查看官网。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939110.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Chrome 132 版本开发者工具(DevTools)更新内容

Chrome 132 版本开发者工具&#xff08;DevTools&#xff09;更新内容 一、使用 Gemini 调试 Network、Source 和 Performance Chrome 131 可以使用 Gemini 调试 CSS&#xff0c;现在可以调试更多模块了 与元素面板中的右键菜单类似&#xff0c;要打开 AI 辅助面板并开始与 …

[白月黑羽]关于风机协议工具的解答

架构 python3.8pyqt5 先来看下原题&#xff1a; 视频中软件的效果 先来看下程序的效果如何&#xff0c;看上去大概相似 对应代码已经上传到了gitcode https://gitcode.com/m0_37662818/fan_protocol_tool/overview 实现中的难点是双悬浮可视化&#xff0c;同时要高亮悬浮对…

使用C#在目录层次结构中搜索文件以查找目标字符串

例程以递归方式搜索目录层次结构中的文件以查找目标字符串。它可以搜索几乎任何类型的文件&#xff0c;即使它不包含 Windows 理解的文本。例如&#xff0c;它可以搜索 DLL 和可执行文件以查看它们是否恰好包含字符串。 下面的代码中显示的ListFiles 方法完成了大部分工作。 …

JAVA爬虫获取1688关键词接口

以下是使用Java爬虫获取1688关键词接口的详细步骤和示例代码&#xff1a; 一、获取API接口访问权限 要使用1688关键词接口&#xff0c;首先需要获取API的使用权限&#xff0c;并了解接口规范。以下是获取API接口的详细步骤&#xff1a; 注册账号&#xff1a;在1688平台注册一…

微服务SpringCloud链路追踪之Micrometer+Zipkin

视频教程&#xff1a; https://www.bilibili.com/video/BV12LBFYjEvR 效果演示 当我们发送一个请求给 Gateway 的时候&#xff0c;由 Micrometer trace 进行链路追踪和数据收集&#xff0c;由 Zipkin 进行数据展示。可以清楚的看到微服务的调用过程&#xff0c;以及每个微服务…

Leetcode 插入区间

class Solution {public int[][] insert(int[][] intervals, int[] newInterval) {List<int[]> result new ArrayList<>();int i 0;// Step 1: 添加所有在 newInterval 之前的区间while(i < intervals.length && intervals[i][1] < newInterval[0]…

CSS|07 标准文档流

标准文档流 一、什么是标准文档流 在制作的 HTML 网页和 PS 画图软件画图时有本质上面的区别: HTML 网页在制作的时候都得遵循一个“流的规则:从左至右、从上至下。 使用 Ps 软件画图时可以在任意地方画图。 <!DOCTYPE html> <html lang"en"> <hea…

redis 缓存使用

工具类 package org.springblade.questionnaire.redis;import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factor…

【排序算法】——选择排序

前言 排序(Sorting) 是计算机程序设计中的一种重要操作&#xff0c;它的功能是将一个数据元素&#xff08;或记录&#xff09;的任意序列&#xff0c;重新排列成一个关键字有序的序列。所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#x…

递归实现指数型枚举(递归)

92. 递归实现指数型枚举 - AcWing题库 每个数有选和不选两种情况 我们把每个数看成每层&#xff0c;可以画出一个递归搜索树 叶子节点就是我们的答案 很容易写出每dfs函数 dfs传入一个u表示层数 当层数大于我们n时&#xff0c;去判断每个数字的选择情况&#xff0c;输出被选…

无限次使用 cursor pro

github地址 cursor-vip 使用方式 在 MacOS/Linux 中&#xff0c;请打开终端&#xff1b; 在 Windows 中&#xff0c;请打开 Git Bash。 然后执行以下命令来安装&#xff1a; 部分电脑可能会误报毒&#xff0c;需要关闭杀毒软件/电脑管家/安全防护再进行 方式1&#xff1a;通过…

【AI热点】小型语言模型(SLM)的崛起:如何在AI时代中找到你的“左膀右臂”?

人工智能模型的演变 多年来&#xff0c;谷歌等科技巨头和OpenAI等初创公司&#xff0c;一直在不遗余力地利用海量在线数据&#xff0c;打造更大、更昂贵的人工智能&#xff08;AI&#xff09;模型。这些大型语言模型&#xff08;LLM&#xff09;被广泛应用于ChatGPT等聊天机器…

解决Nginx + Vue.js (ruoyi-vue) 单页应用(SPA) 404问题的指南

问题描述 在使用Vue.js构建的单页应用&#xff08;SPA&#xff09;中&#xff0c;特别是像ruoyi-vue这样的框架&#xff0c;如果启用了HTML5历史记录模式进行路由管理&#xff0c;那么用户直接访问子路径或刷新页面时可能会遇到404错误。这是因为当用户尝试访问一个非根路径时…

Ubuntu22.04配置3D gaussian splatting

这篇博客提供了3D gaussian splatting在新安装Ubuntu上的配置过程。 1.拉仓库 2.安装显卡驱动和cuda版本 3.安装Pytorch 4.安装Pycharm和配置Python 5.安装附加依赖项&#xff08;方法一&#xff09; 6.安装Anaconda&#xff08;方法二&#xff09; 7.测试 1.拉仓库 # HT…

在 Visual Studio Code 中编译、调试和执行 Makefile 工程 llama2.c

在 Visual Studio Code 中编译、调试和执行 Makefile 工程 llama2.c 1. Installing the extension (在 Visual Studio Code 中安装插件)1.1. Extensions for Visual Studio Code1.2. C/C1.2.1. Pre-requisites 1.3. Makefile Tools 2. Configuring your project (配置项目)2.1.…

深度解析:推荐系统的进化之路与深度学习革命

目录 前深度学习时代一推荐系统的进化之路 浪潮之巅一深度学习在推荐系统中的应用 Embedding 技术在推荐系统中的应用 Embedding的原理 Embedding的分类 Word2vec Item2vec Embedding 与深度学习推荐系统的结合 YouTube 推荐系统召回层 局部敏感哈希 多角度审视推…

MAPTR:在线矢量化高精地图构建的结构化建模与学习(2208)

MAPTR: STRUCTURED MODELING AND LEARNING FOR ONLINE VECTORIZED HD MAP CONSTRUCTION MAPTR&#xff1a;在线矢量化高精地图构建的结构化建模与学习 ABSTRACT High-definition (HD) map provides abundant and precise environmental information of the driving scene, se…

SpringBoot集成Canal实现MySQL实时同步数据到Redis

MySQL增量数据同步利器Canal环境搭建流程 软件环境 JDK17.0.12 canal-server1.1.7 canal-client1.1.7 MySQL5.7 IDEA2024.2.0.2 我们先看Canal1.1.7源码对应的项目结构 1、基于源码编译打包 # 源码下载地址 https://github.com/alibaba/canal # 执行以下命令&#xff0…

嵌入式驱动开发详解16(音频驱动开发)

文章目录 前言WM8960简介I2S协议接口说明 SAI音频接口简介驱动框架简介设备树配置内核使能声卡设置与测试 后续参考文献 前言 该专栏主要是讲解嵌入式相关的驱动开发&#xff0c;但是由于ALSA驱动框架过于复杂&#xff0c;实现音频编解码芯片的驱动不是一个人能完成的&#xf…

OpenGL ES 03 加载3张图片并做混合处理

OpenGL ES 02 加载3张图片并做混合处理 什么是纹理单元纹理单元的作用使用纹理单元的步骤详细解释加载图片并绑定到到GPU纹理单元采样器的设置1.设置采样器变量的纹理单元编号&#xff0c;目的是为了告诉纹理采样器&#xff0c;从哪个纹理单元采集数据2.如果你没有显式地设置采…