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 {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Trace filter   init.......");
        System.out.println("Init Trace filter  init.......");
    }

    @Override
    public 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();
        }
    }

    @Override
    public 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 {
    @Override
    public 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
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }


    @SneakyThrows//异常抛出,相当于方法上throw一个异常
    @Override
    public 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 {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Trace filter   init.......");
    }

    @Override
    public 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();
        }
    }

    @Override
    public 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 {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        // 自定义 ThreadFactory
        ThreadFactory threadFactory = new ThreadFactory() {
            private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
            private final String namePrefix = "Async---";

            @Override
            public 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;
    }
}

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

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

相关文章

SAP RFC 用户安全授权

一、SAP 通讯用户 对于RFC接口的用户&#xff0c;使用五种用户类型之一的“通讯”类型&#xff0c;这种类型的用户没有登陆SAPGUI的权限。 二、对调用的RFC授权 在通讯用户内部&#xff0c;权限对象&#xff1a;S_RFC中&#xff0c;限制进一步可以调用的RFC函数授权&#xff…

Spring IoC——依赖注入

1. 依赖注入的介绍 DI&#xff0c;也就是依赖注入&#xff0c;在容器中建立的 bean &#xff08;对象&#xff09;与 bean 之间是有依赖关系的&#xff0c;如果直接把对象存在 IoC 容器中&#xff0c;那么就都是一个独立的对象&#xff0c;通过建立他们的依赖关系&#xff0c;…

IDEA连接EXPRESS版本的SQL server数据库

我安装的版本是SQL2019-SSEI-Expr.exe 也就是EXPRESS版本的SQL Server安排非常简单和快速。 但是默认没有启动sa用户。 启动sa用户名密码登录 默认安装完以后没有启用。 使用Miscrosoft SQL Server Management Studio 使用Windows身份连接后。 在安全性》登录名中找到sa并修改…

​Java面试经典 150 题.P13. 罗马数字转整数(012)​

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

大数据-204 数据挖掘 机器学习理论 - 混淆矩阵 sklearn 决策树算法评价

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Windows11下将某个程序添加到鼠标右键快捷菜单

经常看log&#xff0c;最喜欢用的txt查看和编辑工具是EditPlus&#xff0c;好像是个韩国软件&#xff0c;最大的优势是打开大文件&#xff0c;好几G的log文件也很轻松&#xff0c;速度快&#xff0c;然后还有各种高亮设置查找文件等&#xff0c;非常方便。但是不知道为什么&…

宏处理将多个excel文件的指定sheet页合并到一个excel文件中

背景了解&#xff1a;有个同事问我&#xff1a;现在他要处理一千多个文件&#xff0c;每个excel文件都有3个sheet页签&#xff0c;想把所有的excel文件的第二个sheet页签复制一份放到一个新的excel文件中。如果是手动去操作一个个文件的复制&#xff0c;也没什么不可&#xff0…

什么是散度,什么是旋度,分别反映什么现象,磁场和静电场分别是什么场?

散度和旋度是矢量场中重要的微分运算概念&#xff0c;用来描述矢量场的局部特性&#xff0c;广泛应用于物理学、流体力学和电磁学中。 1. 散度&#xff08;Divergence&#xff09; 散度描述的是一个矢量场在某一点的“发散”或“汇聚”程度&#xff0c;简单来说就是在该点附近…

web——warmup——攻防世界

这道题还是没有做出来。。&#xff0c;来总结一下 1.ctrlU显示源码 2.看见body里有source.php 打开这个source.php 看见了源码 highlight_file(FILE); 这行代码用于高亮显示当前文件的源码&#xff0c;适合调试和学习&#xff0c;但在生产环境中通常不需要。 class emmm 定义…

vue3项目history模式部署404处理,使用 historyApiFallback 中间件支持单页面应用路由

vue3项目history模式部署404处理&#xff0c;使用 historyApiFallback 中间件支持单页面应用路由 在现代的 web 开发中&#xff0c;单页面应用&#xff08;SPA&#xff09;变得越来越流行。这类应用通常依赖于客户端路由来提供流畅的用户体验&#xff0c;但在服务器端&#xf…

跨境电商平台系统开发

随着全球化的不断深入&#xff0c;跨境电商作为新兴的商业模式&#xff0c;越来越受到企业和消费者的关注。跨境电商平台的系统开发不仅涉及技术层面的挑战&#xff0c;更涉及到法律、物流、支付等多方面的因素。商淘云将分享跨境电商平台系统开发的主要环节&#xff0c;包括需…

《Web性能权威指南》-WebRTC-读书笔记

本文是《Web性能权威指南》第四部分——WebRTC的读书笔记。 第一部分——网络技术概览&#xff0c;请参考网络技术概览&#xff1b; 第二部分——无线网络性能&#xff0c;请参考无线网络性能&#xff1b; 第三部分——HTTP&#xff0c;请参考HTTP&#xff1b; 第四部分——浏览…

.NET 8 Web API 中的身份验证和授权

本次介绍分为3篇文章&#xff1a; 1&#xff1a;.Net 8 Web API CRUD 操作.Net 8 Web API CRUD 操作-CSDN博客 2&#xff1a;在 .Net 8 API 中实现 Entity Framework 的 Code First 方法https://blog.csdn.net/hefeng_aspnet/article/details/143229912 3&#xff1a;.NET …

android定时器循环实现轮播图

说明&#xff1a; android定时器加for循环实现轮播图 效果&#xff1a; step1: package com.example.iosdialogdemo;import android.os.Bundle; import android.os.Handler; import android.widget.ImageView; import android.widget.TextView;import androidx.appcompat.ap…

基于Node.js+Vue+MySQL实现的(Web)图书管理系统

1 需求分析 本图书管理系统主要实现对图书馆的管理&#xff1a;图书、读者、管理员、借阅。由此&#xff0c;结构可分为&#xff1a;图书管理、读者管理、管理员管理、借还管理、罚单管理、还书信息。 1.1 需求定义 1.1.1 图书管理 可对图书信息进行浏览、编辑&#xff08;…

计算机网络803-(5)运输层

目录 一.运输层的两个主要协议&#xff1a;TCP 与 UDP 1.TCP/IP 的运输层有两个不同的协议&#xff1a; 2.端口号(protocol port number) &#xff08;1&#xff09;软件端口与硬件端口 &#xff08;2&#xff09;TCP 的端口 &#xff08;3&#xff09;三类端口 二.用户…

机器学习之fetch_olivetti_faces人脸识别--基于Python实现

fetch_olivetti_faces 数据集下载 fetch_olivetti_faceshttps://github.com/jikechao/olivettifaces sklearn.datasets.fetch_olivetti_faces(*, data_homeNone, shuffleFalse, random_state0, download_if_missingTrue, return_X_yFalse, n_retries3, delay1.0)[source] L…

嵌入式硬件电子电路设计(三)电源电路之负电源

引言&#xff1a;在对信号线性度放大要求非常高的应用需要使用双电源运放&#xff0c;比如高精度测量仪器、仪表等;那么就需要给双电源运放提供正负电源。 目录 负电源电路原理 负电源的作用 如何产生负电源 负电源能作功吗&#xff1f; 地的理解 负电压产生电路 BUCK电…

【SpringMVC】传递json,获取url参数,上传文件

【传递json数据】 【json概念】 一种轻量级数据交互格式&#xff0c;有自己的格式和语法&#xff0c;使用文本表示一个对象或数组的信息&#xff0c;其本质上是字符串&#xff0c;负责在不同的语言中数据传递与交换 json数据以字符串的形式体现 【json字符串与Java对象互转…

web3.0 开发实践

优质博文&#xff1a;IT-BLOG-CN 一、简介 Web3.0也称为去中心化网络&#xff0c;是对互联网未来演进的一种概念性描述。它代表着对现有互联网的下一代版本的设想和期望。Web3.0的目标是通过整合区块链技术、分布式系统和加密技术等新兴技术&#xff0c;构建一个更加去中心化…