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;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言结构方程模型(SEM)

原文链接&#xff1a;R语言结构方程模型&#xff08;SEM&#xff09;https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247624956&idx4&sn295580a016a86cfee8ee2277c93e32d5&chksmfa8da91bcdfa200da897f1f267492039865bdfe5d75a1c6e6df92ff5005e0eb5cc33a…

国标GB28181视频平台EasyCVR私有化视频平台工地防盗视频监控系统方案

一、方案背景 在当代建筑施工领域&#xff0c;安全监管和防盗监控是保障工程顺利进行和资产安全的关键措施。随着科技进步&#xff0c;传统的监控系统已不足以应对现代工地的安全挑战。因此&#xff0c;基于国标GB28181视频平台EasyCVR的工地防盗视频监控系统应运而生&#xf…

labview学习总结

labview学习总结 安装labview的特点一、图形化编程范式二、并行执行机制三、硬件集成能力四、应用领域优势五、开发效率六、系统集成能力**labview基本组成示意图****常用程序结构图解**结语 基础知识介绍界面前后面板的概念平铺式和层叠式 帧的概念结构类型顺序结构for循环whi…

《YOLO 目标检测》—— YOLO v4 详细介绍

文章目录 一、整体网络结构1. YOLO v4 网络结构图2.对之前版本改进创新的概括 二、对改进创新部分的具体介绍1. 输入端创新2. Backbone主干网络创新CSPDarknet53Mish激活函数Dropblock正则化 3. 特征融合创新SPP模块PAN结构 4. Prediction输出层创新CIOU LossDIoU_NMS&#xff…

动态规划 —— dp问题-按摩师

1. 按摩师 题目链接&#xff1a; 面试题 17.16. 按摩师 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/the-masseuse-lcci/description/ 2. 算法原理 状态表示&#xff1a;以某一个位置为结尾或者以某一个位置为起点 dp[i]表示&#xff1a;选择到i位置…

【react】基础知识点学习

1. 创建项目 npm install -g create-react-app npx create-react-app my-app cd my-app npm startindex.js为入口文件&#xff0c;App.js为根组件。 如何将react应用挂载在页面上&#xff1f; 将App组件渲染到id为root的DOM元素中 2. JSX JSX是|avaScript和XML(HTML)的缩写…

word及Excel常见功能使用

最近一直在整理需规文档及表格&#xff0c;Word及Excel需要熟练使用。 Word文档 清除复制过来的样式 当复制文字时&#xff0c;一般会带着字体样式&#xff0c;此时可选中该文字 并使用 ctrlshiftN 快捷键进行清除。 批注 插入->批注&#xff0c;选中文本 点击“批注”…

在Microsoft Outlook日历中添加多个时区

在Microsoft Outlook日历中添加多个时区 1.单击Outlook中的文件选项卡&#xff0c;单击选项 2.左侧菜单中选择日历 3.向下滚动到时区部分&#xff0c;并标记当前时区&#xff0c;比如China 4.选中“显示第二个时区”框 5.选择第二个时区并给它一个标签&#xff0c;比如Germa…

vue组件在项目中的常用业务逻辑(3)

获取完后台接口数据后&#xff0c;需将数据在页面中进行动态展示。 一、在getters中简化数据&#xff1a; 二、在search>index.vue中写计算属性&#xff0c;实现将接口的goodsList模块数据展示在vue的search上&#xff1a; 三、1.用v-for循环数据&#xff0c;一共十个&…

从传感器到清洁力提升,灵途科技推动家电智能化发展

双十一第一波预售刚结束&#xff0c;第二波预售活动又火热开启。得益于政府补贴和平台优惠的双重带动&#xff0c;扫地机等高端智能家电需求暴增。 今年&#xff0c;各大电商平台不再单纯追求“全网最低价”&#xff0c;而是在低价基础上注重提升用户的综合体验。家电机器人品…

基于STM32的智能温室环境监测与控制系统设计(代码示例)

一、项目概述 在现代农业中&#xff0c;智能大棚能够通过环境监测、数据分析和自动控制等技术手段&#xff0c;实现对作物生长环境的精细化管理。本项目旨在设计一个基于STM32单片机的智能大棚系统&#xff0c;能够实时监测光照强度、空气温湿度及土壤湿度&#xff0c;并根据设…

详解CRC校验原理以及FPGA实现

文章目录 一、什么是CRC校验&#xff1f;二、实现CRC校验原理以及步骤2.1 用多项式表示二元码数据2.2 选择一个生成多项式作为校验2.3 计算CRC校验码 三、CRC判断数据是否错误的原理以及步骤3.1 将收到的数据与生成多项式求余3.2 数据发生错误再进行CRC校验判断 四、FPGA实现CR…

大模型学习笔记------CLIP模型解读与思考

大模型学习笔记------CLIP模型解读与思考 1、为什么提出CLIP模型2、CLIP模型详解3、CLIP模型的重要意义 上文已经讲到&#xff0c;我认为多模态的大模型是最有前途的大模型&#xff0c;首先学习的就是CLIP这个模型。 CLIP&#xff08;Contrastive Language-Image Pretraining&…

《IMM交互式多模型滤波MATLAB实践》专栏目录,持续更新……

专栏链接&#xff1a;https://blog.csdn.net/callmeup/category_12816762.html 专栏介绍 关于IMM的例程 双模型EKF&#xff1a; 【逐行注释】基于CV/CT模型的IMM|MATLAB程序|源代码复制后即可运行&#xff0c;无需下载三模型EKF&#xff1a; 【matlab代码】3个模型的IMM例程&…

【UART异步串口协议及verilog实现】

UART异步串口协议 1 UART1.1 数据格式1.2 波特率 2 UART的发送和接收模块2.1 uart发送模块2.2 uart的接收模块 【博客首发于微信公众号《 漫谈芯片与编程》】 本篇文章介绍常用的芯片外围低速协议&#xff1a;UART&#xff1b; 1 UART UART是异步串行传输协议&#xff0c;即…

TS-AWG控制电光调制器:推动科技应用新发展的利器

一、电光调制有什么用&#xff1f; 如今&#xff0c;基于光学、光子学和脉冲激光以及电光调制器的应用正变得极为流行&#xff0c;最新一代科学家正在为其实际应用开辟新领域&#xff0c;如汽车激光雷达、医疗解决方案、航空航天和国防、量子和激光传感器。 测试挑战、上市时…

ros与mqtt相互转换

vda5050 VDA5050协议介绍 和 详细翻译-CSDN博客 ros与mqtt相互转换 如何转换的&#xff0c;通过某个中转包&#xff0c;获取ros的消息然后以需要的格式转换为mqtt 需要的参数 ros相关 parameters[ (ros_subscriber_type, vda5050_msgs/NodeState), (ros_subscriber_queue…

探讨 | 大模型在传统NLP任务的使用姿势

以chatgpt为起始节点的大模型技术经过了两年左右的发展&#xff0c;大致沉淀了一些较为确定的研究领域。首先是基座大模型的研究&#xff0c;主要是国内外大厂集中大量财力、人力、算力进行大模型军备竞赛&#xff0c;企图将大模型的基础能力&#xff08;包括推理能力&#xff…

C语言 — 指针的进阶

文章目录 前言一、字符指针二、指针数组三、数组指针数组名 与 &数组名 四、指针传参二维数组传参 五、函数指针结语 前言 通过前面的关于指针的学习&#xff0c;我们了解了指针的一些个特性。本篇文章我们将深入指针&#xff0c;挖掘指针更深处的知识。 在开始之前&#…

Ceisum无人机巡检视频投放

公司投标内容有个视频投放的功能动画&#xff0c;原本想实现这么一个效果&#xff1a; 案例效果来自别人的展示作品&#xff0c;Leader一眼就相中了这个效果&#xff0c;可惜别人的终究是别人的&#xff0c;又不会白白给你&#xff0c;终究是要自己动手尝试。 动画方面的展示…