当前位置: 首页 > news >正文

springcloud通过MDC实现分布式链路追踪

在DDD领域驱动设计中,我们使用SpringCloud来去实现,但排查错误的时候,通常会想到Skywalking,但是引入一个新的服务,增加了系统消耗和管理学习成本,对于大型项目比较适合,但是小的项目显得太过臃肿了,我们此时就可以使用TraceId,将其存放到MDC中,返回的时候参数带上它,访问的时候日志打印出来,每次访问生成的TraceId不同,这样可以实现分布式链路追踪的问题。

通用部分

封装TraceIdUtil工具类

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import cn.hutool.core.util.IdUtil;public class TraceIdUtil {public static final String TRACE_ID_KEY = "TraceId";/*** 生成TraceId* @return*/public static String generateTraceId(){String traceId = IdUtil.fastSimpleUUID().toUpperCase();MDC.put(TRACE_ID_KEY,traceId);return traceId;}/*** 生成TraceId* @return*/public static String generateTraceId(String traceId){if(StringUtils.isBlank(traceId)){return generateTraceId();}MDC.put(TRACE_ID_KEY,traceId);return traceId;}/*** 获取TraceId* @return*/public static String getTraceId(){return MDC.get(TRACE_ID_KEY);}/*** 移除TraceId* @return*/public static void removeTraceId(){MDC.remove(TRACE_ID_KEY);}
}

logback.xml日志文件的修改

<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [TRACEID:%X{TraceId}]  [%thread] %-5level %logger{36} -%msg%n</Pattern>

需注意:
在这里插入图片描述

biff 模块

创建过滤器

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import com.karry.admin.bff.common.util.TraceIdUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;@Slf4j
@WebFilter
public class TraceFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("Init Trace filter   init.......");System.out.println("Init Trace filter  init.......");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {try {HttpServletRequest servletRequest = (HttpServletRequest) request;String gateWayTraceId = ((HttpServletRequest) request).getHeader(TraceIdUtil.TRACE_ID_KEY);String traceId = TraceIdUtil.generateTraceId(StringUtils.isEmpty(gateWayTraceId)? IdUtil.fastSimpleUUID().toUpperCase(): gateWayTraceId);// 创建新的请求包装器log.info("TraceIdUtil.getTraceId():"+TraceIdUtil.getTraceId());//将请求和应答交给下一个处理器处理filterChain.doFilter(request,response);}catch (Exception e){e.printStackTrace();}finally {//最后移除,不然有可能造成内存泄漏TraceIdUtil.removeTraceId();}}@Overridepublic void destroy() {log.info("Init Trace filter  destroy.......");}
}

配置过滤器生效

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.karry.admin.bff.common.filter.TraceFilter;
import lombok.extern.slf4j.Slf4j;@Slf4j
@Configuration
public class WebConfiguration {@Bean@ConditionalOnMissingBean({TraceFilter.class})@Order(Ordered.HIGHEST_PRECEDENCE + 100)public FilterRegistrationBean<TraceFilter> traceFilterBean(){FilterRegistrationBean<TraceFilter> bean = new FilterRegistrationBean<>();bean.setFilter(new TraceFilter());bean.addUrlPatterns("/*");return bean;}
}

figen接口发送的head修改

此处修改了发送的请求的header,在其他模块就可以获取从biff层生成的traceId了。

import org.springframework.context.annotation.Configuration;
import com.karry.admin.bff.common.util.TraceIdUtil;
import feign.RequestInterceptor;
import feign.RequestTemplate;@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template){String traceId = TraceIdUtil.getTraceId();//当前线程调用中有traceId,则将该traceId进行透传if (traceId != null) {template.header(TraceIdUtil.TRACE_ID_KEY,TraceIdUtil.getTraceId());}}
}

统一返回处理

此种情况时针对BaseResult,,这种统一返回的对象无法直接修改的情况下使用的,如果可以直接修改:

    /*** 链路追踪TraceId*/public String traceId = TraceIdUtil.getTraceId();

不可以直接修改就用响应拦截器进行处理:

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.karry.app.common.utils.TraceIdUtil;
import com.karry.order.sdk.utils.BeanCopyUtils;
import com.souche.platform.common.model.base.BaseResult;
import lombok.SneakyThrows;@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {/*** 开关,如果是true才会调用beforeBodyWrite*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows//异常抛出,相当于方法上throw一个异常@Overridepublic Object beforeBodyWrite(Object object, MethodParameter methodParameter, MediaType mediaType, Class aClass,ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {BaseResult result = BeanCopyUtils.copy(object, BaseResult.class);result.setTraceId(TraceIdUtil.getTraceId());return result;}}

非biff模块

创建过滤器

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import com.karry.app.common.utils.TraceIdUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;@Slf4j
@WebFilter
public class TraceFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("Init Trace filter   init.......");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {try {HttpServletRequest servletRequest = (HttpServletRequest) request;String gateWayTraceId = ((HttpServletRequest) request).getHeader(TraceIdUtil.TRACE_ID_KEY);String traceId = TraceIdUtil.generateTraceId(StringUtils.isEmpty(gateWayTraceId)? IdUtil.fastSimpleUUID().toUpperCase(): gateWayTraceId);//将请求和应答交给下一个处理器处理filterChain.doFilter(request,response);}catch (Exception e){e.printStackTrace();}finally {//最后移除,不然有可能造成内存泄漏TraceIdUtil.removeTraceId();}}@Overridepublic void destroy() {log.info("Init Trace filter  destroy.......");}
}

配置过滤器生效

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.karry.admin.bff.common.filter.TraceFilter;
import lombok.extern.slf4j.Slf4j;@Slf4j
@Configuration
public class WebConfiguration {@Bean@ConditionalOnMissingBean({TraceFilter.class})@Order(Ordered.HIGHEST_PRECEDENCE + 100)public FilterRegistrationBean<TraceFilter> traceFilterBean(){FilterRegistrationBean<TraceFilter> bean = new FilterRegistrationBean<>();bean.setFilter(new TraceFilter());bean.addUrlPatterns("/*");return bean;}
}

线程池

上面对于单线程的情况可以进行解决,traceId和Threadlocal很像,是键值对模式,会有内存溢出问题,还是线程私有的。 所以在多线程的情况下就不能获取主线程的traceId了。我们就需要设置线程工厂包装 Runnable 来解决这个问题。

import org.slf4j.MDC;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Configuration
public class MyThreadPoolConfig {@Beanpublic ThreadPoolExecutor threadPoolExecutor() {// 自定义 ThreadFactoryThreadFactory threadFactory = new ThreadFactory() {private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();private final String namePrefix = "Async---";@Overridepublic Thread newThread(Runnable r) {// 获取主线程的 MDC 上下文Map<String, String> contextMap = MDC.getCopyOfContextMap();// 包装 Runnable 以设置 MDC 上下文Runnable wrappedRunnable = () -> {try {// 设置 MDC 上下文MDC.setContextMap(contextMap);// 执行任务r.run();} finally {// 清除 MDC 上下文MDC.clear();}};Thread thread = defaultFactory.newThread(wrappedRunnable);thread.setName(namePrefix + thread.getName());return thread;}};ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,30L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(500),threadFactory,new ThreadPoolExecutor.CallerRunsPolicy());return executor;}
}

相关文章:

springcloud通过MDC实现分布式链路追踪

在DDD领域驱动设计中&#xff0c;我们使用SpringCloud来去实现&#xff0c;但排查错误的时候&#xff0c;通常会想到Skywalking&#xff0c;但是引入一个新的服务&#xff0c;增加了系统消耗和管理学习成本&#xff0c;对于大型项目比较适合&#xff0c;但是小的项目显得太过臃…...

logback日志级别动态切换四种方案

生产环境中经常有需要动态修改日志级别。 现在就介绍几种方案 方案一&#xff1a;开启logback的自动扫描更新 配置如下 <?xml version"1.0" encoding"UTF-8"?> <configuration scan"true" scanPeriod"60 seconds" debug…...

AI视频管理平台中使用目标检测模型中的NMS参数原理及设置原则

目标检测模型中的NMS参数原理及设置原则 在目标检测模型中&#xff0c;非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;简称NMS&#xff09;是一种常用的后处理技术&#xff0c;用于筛选和保留最佳的检测框。本文将详细介绍NMS的原理、参数设置原则以及实际应用…...

从零开始点亮一个LED灯 —— keil下载、新建工程、版本烧录、面包板使用、实例代码

一、keil下载 参考视频&#xff1a;Keil5安装教程视频 (全套资料51和32皆可用Keil5编译设置)_哔哩哔哩_bilibili 视频内容包括下载链接、安装教程、库导入&#xff0c;非常详细&#xff01; 二、新建工程 2.1.使用stm32CubeMX新建工程 10. 使用STM32CubeMX新建工程 — [野…...

[pdf,epub]105页《分析模式》漫谈合集01

105页的《分析模式》漫谈合集第1集的pdf、epub文件&#xff0c;已上传到本账号的CSDN资源。 如果无法下载&#xff0c;也可以访问umlchina.com/url/ap.html 已排版成适合手机阅读&#xff0c;pdf的排版更好一些。 ★UMLChina为什么叒要翻译《分析模式》&#xff1f; ★[缝合故…...

计算机网络5层模型

应用层常见协议 DNS协议 作用:用于实现网络设备名字到IP地址映射的网络服务 特点:DNS是因特网使用的命名系统&#xff0c;它将人们易于记忆的主机名(如www.example.com)转换为机器可识别的IP地址。 FTP协议 作用:用于实现交互式文件传输功能。 特点:FTP支持Standard(主动…...

Python毕业设计选题:基于Python的无人超市管理系统-flask+vue

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 系统首页 超市商品详情 购物车 我的订单 管理员登录界面 管理员功能界面 用户界面 员…...

WindowsDocker安装到D盘,C盘太占用空间了。

Windows安装 Docker Desktop的时候,默认位置是安装在C盘,使用Docker下载的镜像文件也是保存在C盘,如果对Docker使用评率比较高的小伙伴,可能C盘空间,会被耗尽,有没有一种办法可以将Docker安装到其它磁盘,同时Docker的数据文件也保存在其他磁盘呢? 答案是有的,我们可以…...

Java面试经典 150 题.P80. 删除有序数组中的重复项 II(004)

本题来自&#xff1a;力扣-面试经典 150 题 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台https://leetcode.cn/studyplan/top-interview-150/ 题解&#xff1a; class Solution {public int removeDuplicates(int[] nums)…...

【Three.js】SpriteMaterial 加载图片泛白,和原图片不一致

解决方法 如上图所示&#xff0c;整体泛白了&#xff0c;解决方法如下&#xff0c;添加 material.map.colorSpace srgb const imgTexture new THREE.TextureLoader().load(imgSrc)const material new THREE.SpriteMaterial({ map: imgTexture, transparent: true, opacity:…...

了解神经网络中的激活函数

一、激活函数的特征 非线性&#xff0c;激活函数必须是非线性函数。可微性&#xff0c;训练网络模型时&#xff0c;基于梯度的模型最优化方法要求激活函数必须是可导的。单调性&#xff0c;单调递增或单调递减&#xff0c;单调函数保证模型的简单。隐藏层一般需要使用激活函数…...

配置BGP与IGP交互和路由自动聚合示例

组网需求 如图所示&#xff0c;用户将网络划分为AS65008和AS65009&#xff0c;在AS65009内&#xff0c;使用IGP协议来计算路由&#xff08;该例使用OSPF做为IGP协议&#xff09;。要求实现两个AS之间的互相通信。 配置思路 采用如下的思路配置BGP与IGP交互&#xff1a; 在AR…...

代码随想录算法训练营第三十三天 | 62.不同路径 63.不同路径

LeetCode 62.不同路径&#xff1a; 文章链接 题目链接&#xff1a;62.不同路径 思路&#xff1a; 动态规划 使用二维数组保存递推结果 ① dp数组及下标含义 dp[i][j]&#xff1a;表明从(0, 0)到下标为(i, j)的点有多少条不同的路径 ② 递推式&#xff1a; 机器人只能向下或向…...

使用Flask构建RESTful API

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Flask构建RESTful API Flask简介 环境搭建 安装Flask 项目结构 创建应用 路由定义 请求处理 获取查询参数 获取请求体 响应…...

基于springboot的Java学习论坛平台

基于springboot的Java学习论坛平台 摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括学习平台的网络应用&#xff0c;在外国学习平台已经是很普遍的方式&#xff0c;不过国内的管理平台可能还处于起步阶段。学习平台具…...

Python离线环境搭建

引言 在软件开发过程中&#xff0c;我们常常会遇到内网环境无法直接访问外网的情况&#xff0c;这就需要我们通过一些特殊手段来搭建Python开发环境。本文将详细介绍如何利用U盘在内网机与外网机之间迁移Python环境及其依赖包。 工具准备 1台内网机1台外网机1个U盘 操作步骤…...

windows下kafka使用出现的问题

kafka启动 启动kafka需要先启动zookeeper&#xff0c;在kafka根目录下先启动zookeeper .\bin\windows\zookeeper-server-start.bat .\config\zookeeper.properties启动kafka 另开一个cmd命令行 .\bin\windows\kafka-server-start.bat .\config\server.propertieskafka与jdk版…...

ctfshow文件包含web78~81

目录 web78 方法一&#xff1a;filter伪协议 方法二&#xff1a;input协议 方法三&#xff1a;data协议 web79 方法一:input协议 方法二&#xff1a;data协议 web80 方法一&#xff1a;input协议 方法二&#xff1a;日志包含getshell web81 web78 if(isset($_GET[file]…...

鸿蒙生态认识

好的&#xff0c;让我们更深入地探讨鸿蒙生态的发展机遇、面临的挑战&#xff0c;以及未来的潜力。 对鸿蒙生态的认知与分析 鸿蒙系统作为一种新兴的操作系统&#xff0c;旨在打破设备之间的壁垒&#xff0c;打造一个更加连通的生态环境。以下是对其崛起的进一步分析&#xf…...

Hadoop-004-Big Data Tools插件的使用

一、Big Data Tools插件配置流程 1、安装Big Data Tools插件 以IntelliJ IDEA 2024.2.3为例打开setting, 搜索安装Big Data Tools插件后重启IDEA 2、Windows系统基础配置 Windows系统需要做一些基础设置&#xff0c;配合插件使用,将之前下载的hadoop-3.2.4.tar.gz 解压到D…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...