天天看點

日志架構 - 基于spring-boot - 實作5 - 線程切換

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