• springboot链路追踪ID


    概述:

    1、实现拦截器,请求开始时候创建MDC值,结束后清理MDC值

    2、实现线程池,将MDC上下文从主线程传递给子线程

    具体实现:

    1、拦截器请求的进来和结束的时候设置hash值:

    import org.jetbrains.annotations.NotNull;
    import org.slf4j.MDC;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.UUID;
    
    /**
     * 拦截器
     *
     * @author 许伟强51189
     * @date 2022/5/25 10:41
     */
    public class TraceIdInterceptor implements HandlerInterceptor {
    
        public static final String UNIQUE_ID = "TRACE_ID";
    
        public boolean preHandle(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse, @NotNull Object o) {
            MDC.put(UNIQUE_ID, UUID.randomUUID().toString());
            return true;
        }
    
        public void postHandle(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse, @NotNull Object o, ModelAndView modelAndView) {
        }
    
        public void afterCompletion(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse, @NotNull Object o, Exception e) {
            MDC.clear();
        }
    }

    2、注册拦截器:

    import org.jetbrains.annotations.NotNull;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * TraceID的拦截器\生成請求的唯一值
     *
     * @author 许伟强51189
     * @date 2022/5/25 10:41
     */
    @Configuration
    public class TraceIdInterceptorConfiguration implements WebMvcConfigurer {
    
        @Value("${spring.traceIdInterceptor.enable:false}")
        private Boolean enable;
    
        @Value("${spring.traceIdInterceptor.addPathPatterns:/**}")
        private String[] addPathPatterns;
    
        @Value("${spring.traceIdInterceptor.excludePathPatterns:}")
        private String[] excludePathPatterns;
    
        @Override
        public void addInterceptors(@NotNull InterceptorRegistry interceptorRegistry) {
            if (!enable) {
                return;
            }
            TraceIdInterceptor myInterceptor = new TraceIdInterceptor();
            interceptorRegistry.addInterceptor(myInterceptor).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);
        }
    }

    3、实现线程池,用于解决异步线程时候 @Async("name") 的 traceId 丢失问题

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.ThreadPoolExecutor;
    
    /**
     * MdcThreadPoolConfiguration - 用于链路追踪MDC的线程池\在多线程情况下会将主线程的上下文传递给子线程
     *
     * @author 许伟强51189
     * @date 2022/5/25 16:50
     */
    @EnableAsync
    @Configuration
    public class MdcThreadPoolConfiguration {
        private int corePoolSize = 50;
        private int maxPoolSize = 200;
        private int queueCapacity = 1000;
        private int keepAliveSeconds = 300;
    
        @Bean(name = "threadPoolTaskExecutor")
        public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setMaxPoolSize(maxPoolSize);
            executor.setCorePoolSize(corePoolSize);
            executor.setQueueCapacity(queueCapacity);
            executor.setKeepAliveSeconds(keepAliveSeconds);
            executor.setTaskDecorator(new MdcTaskDecorator());
            // 线程池对拒绝任务(无线程可用)的处理策略
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    }

    4、线程池上下文传递

    /**
     * 任务适配器
     *
     * @author 许伟强51189
     * @version 2.0.0
     * @date 2022/5/25 15:36
     */
    public class MdcTaskDecorator implements TaskDecorator {
        /**
         * 使异步线程池获得主线程的上下文
         *
         * @param runnable
         * @return
         */
        @Override
        public Runnable decorate(Runnable runnable) {
            Map<String, String> map = MDC.getCopyOfContextMap();
            return () -> {
                try {
                    MDC.setContextMap(map);
                    runnable.run();
                } finally {
                    MDC.clear();
                }
            };
        }
    }

    5、验证可用(当指定线程池 threadPoolTaskExecutor 的时候 MDC 的值不会丢失,不指定的时候会丢失):

    @Async("threadPoolTaskExecutor")
        public void getUserListAsyncThree(int page, int limit, String hash) {
            log.info("getUserListAsyncThree Thread Name:{}", Thread.currentThread().getName());
            System.out.println(MDC.get(TraceIdInterceptor.UNIQUE_ID));
        }

     如果使用同一个线程池则可以获取到MDC的值否则不可以

    参考博客:

    https://jishuin.proginn.com/p/763bfbd69eeb

  • 相关阅读:
    bilibili 高并发实时弹幕系统的实现
    主流深度学习框架对比
    完整的视频直播系统
    一个简单的直播demo for java
    流媒体知识 wiki
    Entity Framework 6 暂停重试执行策略
    泛型的资源释放
    Entity Framework异步查询和保存
    Entity Framework中的连接管理
    了解Entity Framework中事务处理
  • 原文地址:https://www.cnblogs.com/xuweiqiang/p/16310153.html
Copyright © 2020-2023  润新知