在项目中需要异步执行任务方法,但是异步执行时会使用另一个线程。因为MDC的参数时基于ThreadLocal的,每个线程都保留一份。这样就造成异步线程的日志id没有或者跟前一个日志id不一致,不便于查询日志。
解决方法:
自定义任务装饰器,并配置到线程池
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(10);
//配置最大线程数
executor.setMaxPoolSize(20);
//配置队列大小
executor.setQueueCapacity(10000);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("XXX-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 异步MDC
executor.setTaskDecorator(new MdcTaskDecorator());
//执行初始化
executor.initialize();
return executor;
}
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
// Right now: Web thread context !
// (Grab the current thread MDC data)
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
// Right now: @Async thread context !
// (Restore the Web thread context's MDC data)
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
}