日志框架系列讲解文章
日志框架 - 基于spring-boot - 使用入门
日志框架 - 基于spring-boot - 设计
日志框架 - 基于spring-boot - 实现1 - 配置文件
日志框架 - 基于spring-boot - 实现2 - 消息定义及消息日志打印
日志框架 - 基于spring-boot - 实现3 - 关键字与三种消息解析器
日志框架 - 基于spring-boot - 实现4 - HTTP请求拦截
日志框架 - 基于spring-boot - 实现5 - 线程切换
日志框架 - 基于spring-boot - 实现6 - 自动装配
上一篇我们讲了框架实现的第四部分:实现HTTP请求的拦截
本篇主要讲框架实现的第五部分:如何在线程切换时保留上下文信息。
由于 Logback 的 MDC 实际上是一个 ThreadLocal 的实现(参考这里),因此,当异步执行产生线程切换时,需要将 MDC 保存的信息进行切换。
为了实现此功能,我研究了 Spring 的异步执行组件,发现 Spring 中有一个可用的线程装饰器TaskDecorator。这个是Spring Core 4.3版本才加入的接口,在Spring的文档中没有提及,也没有提供任何实现,但正好是我所需要的功能。代码如下。
/**
* 解决异步执行时MDC内容延续的问题
*/
public class MDCTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
return new MDCContinueRunableDecorator(runnable);
}
/**
* 执行线程装饰器
*/
protected class MDCContinueRunableDecorator implements Runnable {
private final Runnable delegate;
protected final Map<String, String> logContextMap;
public MDCContinueRunableDecorator(Runnable runnable) {
this.delegate = runnable;
this.logContextMap = MDC.getCopyOfContextMap();
}
@Override
public void run() {
MDC.setContextMap(this.logContextMap);
this.delegate.run();
MDC.clear();
}
}
}
然后,需要自定义实现一个 TaskExecutor,替换Spring提供的默认实现,代码如下。
/**
* 自定义线程池
* <p>
* 用于线程切换时的MDC延续
*/
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(maxPoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setTaskDecorator(new MDCTaskDecorator());
executor.setThreadNamePrefix("MDCAdaptTaskExcutor-");
executor.initialize();
return executor;
}
至此,线程切换时的问题已经解决。只要异步处理使用了自定义的 TaskExecutor ,即可实现上下文的自动传递。