关于利用MDC来实现上下文请求链路追踪,具体可以看我之前写的博客上下文追踪
由于MDC内部使用的是ThreadLocal来在线程中传递上下文信息的,但是我们往往会在代码中使用异步操作,这个时候,父线程的ThreadLocal信息是无法传递给子线程的(阿里开源的inheritableThreadLocal)。这个时候需要用到线程池的装饰器来解决这个问题。
RpcContext和httpHeader都可以用这个装饰器解决多线程传递问题。
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
// Right now: @Async thread context !
// (Restore the Web thread context's MDC data)
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
if (context != null){
RequestContextHolder.setRequestAttributes(context);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
}
然后在你的异步线程池配置文件中加上装饰器。我用的是Spring集合的@Async来实现异步的,加装饰器的代码如下:
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {
@Bean("asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
threadPool.setCorePoolSize(30);
threadPool.setMaxPoolSize(500);
threadPool.setWaitForTasksToCompleteOnShutdown(true);
threadPool.setAwaitTerminationSeconds(60 * 15);
threadPool.setThreadFactory(
new ThreadFactoryBuilder().setNameFormat("async-thread-pool-%d").build());
//指定装饰器
threadPool.setTaskDecorator(new MdcTaskDecorator());
return threadPool;
}
@Override
public Executor getAsyncExecutor() {
return asyncExecutor();
}
}