場景:在生產(chǎn)環(huán)境種乙帮,查詢發(fā)現(xiàn)錯(cuò)誤日志時(shí),需要定位跟蹤到最終問題极景,但是項(xiàng)目種用到了很多的異步線程池察净,導(dǎo)致定位問題時(shí)根據(jù)traceId,只能找到一部分日志內(nèi)容盼樟,無法最終定位到問題所在氢卡;
原因:這是由于MDC的實(shí)現(xiàn)是通過ThreadLocal實(shí)現(xiàn)的,然后線程池種的線程和用戶線程不是同一個(gè)線程晨缴,所以在異步調(diào)用的時(shí)候译秦,用戶線程的內(nèi)容沒有同步到線程池線程中,導(dǎo)致最終沒有打印出關(guān)聯(lián)的tracId,從而導(dǎo)致最終無法關(guān)聯(lián)相關(guān)日志筑悴,無法定位到具體問題们拙;
解決方案:因?yàn)閷?shí)現(xiàn)異步的方式有很多種,例如線程池和ThreadPoolExecutor和ThreadPoolTaskExecutor阁吝,spring的@Aync等砚婆,但是根本原因都是因?yàn)閮蓚€(gè)線程不是同一個(gè)線程引起的,所以我這里只例舉線程池ThreadPoolExecutor的實(shí)現(xiàn)突勇,其他實(shí)現(xiàn)相差無幾装盯;
ThreadMdcUtil.class
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* className:ThreadMdcUtil
* description: mdcUtil
*
* @author: david
* date: 2020/12/31 10:42 上午
*/
public class ThreadMdcUtil {
public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
try {
return callable.call();
} finally {
MDC.clear();
}
};
}
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
try {
runnable.run();
} finally {
MDC.clear();
}
};
}
}
ThreadPoolExecutorMdcWrapper.class
import org.slf4j.MDC;
import java.util.concurrent.*;
/**
* className:ThreadPoolExecutorMdcWrapper
* description: 線程池包裝類
*
* @author: david
* date: 2020/12/31 10:41 上午
*/
public class ThreadPoolExecutorMdcWrapper extends ThreadPoolExecutor {
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
public void execute(Runnable task) {
super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()), result);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
}
具體使用方式如下:
在需要使用線程池的時(shí)候,使用ThreadPoolExecutorMdcWrapper類創(chuàng)建線程池去執(zhí)行相關(guān)邏輯甲馋;