异步线程获取上下文的MDC信息

异步线程traceId如何传递_new thread子线程传递traceid-CSDN博客

 

1. 重写线程池,来实现自定义线程池

a.下面的工具类,分别在Callable和Runnable异步任务执行前通过MDC.setContextMap(context)设置请求映射上下文

 1 import org.slf4j.MDC;
 2 import org.springframework.util.CollectionUtils;
 3  
 4 import java.util.Map;
 5 import java.util.concurrent.Callable;
 6  
 7 /**
 8  * @desc: 定义MDC工具类,支持Runnable和Callable两种,目的就是为了把父线程的traceId设置给子线程
 9  */
10 public class MdcUtil {
11  
12     public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
13         return () -> {
14             if (CollectionUtils.isEmpty(context)) {
15                 MDC.clear();
16             } else {
17                 MDC.setContextMap(context);
18             }
19             try {
20                 return callable.call();
21             } finally {
22                 // 清除子线程的,避免内存溢出,就和ThreadLocal.remove()一个原因
23                 MDC.clear();
24             }
25         };
26     }
27  
28     public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
29         return () -> {
30             if (CollectionUtils.isEmpty(context)) {
31                 MDC.clear();
32             } else {
33                 MDC.setContextMap(context);
34             }
35             try {
36                 runnable.run();
37             } finally {
38                 MDC.clear();
39             }
40         };
41     }
42 }

b.下面定义一个ThreadPoolMdcExecutor 类来继承ThreadPoolTaskExecutor 类,重写execute和submit方法

 1 import java.util.concurrent.Callable;
 2 import java.util.concurrent.Future;
 3  
 4 /**
 5  * @desc: 把当前的traceId透传到子线程特意加的实现。
 6  *   重点就是 MDC.getCopyOfContextMap(),此方法获取当前线程(父线程)的traceId
 7  */
 8 public class ThreadPoolMdcExecutor extends ThreadPoolTaskExecutor {
 9     @Override
10     public void execute(Runnable task) {
11         super.execute(MdcUtil.wrap(task, MDC.getCopyOfContextMap()));
12     }
13  
14     @Override
15     public Future<?> submit(Runnable task) {
16         return super.submit(MdcUtil.wrap(task, MDC.getCopyOfContextMap()));
17     }
18  
19     @Override
20     public <T> Future<T> submit(Callable<T> task) {
21         return super.submit(MdcUtil.wrap(task, MDC.getCopyOfContextMap()));
22     }
23 }

c.创建线程池Bean

 1     @Bean(name = "callBackExecutorConfig")
 2     public Executor callBackExecutorConfig() {
 3         ThreadPoolTaskExecutor executor = new ThreadPoolMdcExecutor();
 4         // 配置核心线程数
 5         executor.setCorePoolSize(10);
 6         // 配置最大线程数
 7         executor.setMaxPoolSize(20);
 8         // 配置队列大小
 9         executor.setQueueCapacity(200);
10         // 配置线程池中的线程的名称前缀
11         executor.setThreadNamePrefix("async-Thread-");
12         // rejection-policy:当pool已经达到max size的时候,如何处理新任务
13         // abort:在调用executor执行的方法中抛出异常 RejectedExecutionException
14         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
15         // 执行初始化
16         executor.initialize();
17         return executor;
18     }

2.对默认线程池进行增强

a.通过实现TaskDecorator 接口来增强线程池

 1 public class ContextTransferTaskDecorator implements TaskDecorator {
 2     @Override
 3     public Runnable decorate(Runnable runnable) {
 4         Map<String, String> context = MDC.getCopyOfContextMap();
 5 
 6 //        //子线程调用RequestContextHolder.currentRequestAttributes()会throw new IllegalStateException
 7 //        //需要在执行子线程之前,在主线程中设置属性子线程共享
 8 //        //ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 9 //        //RequestContextHolder.setRequestAttributes(servletRequestAttributes,true);
10 //        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
11         return () -> {
12             try {
13                 MDC.setContextMap(context);
14 //                RequestContextHolder.setRequestAttributes(requestAttributes);
15                 runnable.run();
16             } finally {
17                 //防止线程内部MDC的内存泄漏,此处清除的是线程内部的MDC不影响主线程MDC信息
18                 MDC.clear();
19 //                RequestContextHolder.resetRequestAttributes();
20             }
21         };
22     }
23 }

2.默认线程池的ThreadPoolTaskExecutor.setTaskDecorator

 1     @Bean(name = "callBackExecutorConfig")
 2     public Executor callBackExecutorConfig() {
 3         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor ();
 4         // 配置核心线程数
 5         executor.setCorePoolSize(10);
 6         // 配置最大线程数
 7         executor.setMaxPoolSize(20);
 8         // 配置队列大小
 9         executor.setQueueCapacity(200);
10         // 配置线程池中的线程的名称前缀
11         executor.setThreadNamePrefix("async-Thread-");
12         // rejection-policy:当pool已经达到max size的时候,如何处理新任务
13         // abort:在调用executor执行的方法中抛出异常 RejectedExecutionException
14         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
15         //线程池增强
16         threadPoolTaskExecutor.setTaskDecorator(new ContextTransferTaskDecorator());
17         // 执行初始化
18         executor.initialize();
19         return executor;
20     }

 

posted @ 2024-04-11 11:26  忙碌了一整天的L师傅  阅读(81)  评论(0编辑  收藏  举报