日志架構系列講解文章 日志架構 - 基于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 ,即可實作上下文的自動傳遞。