业务线程池不丢 traceId 的方法
# 1 背景
通常,我们会在日志中,通过 traceId 来进行一次完整请求日志的定位,而 traceId 是保存在 MDC 的本地线程中,当主线程开启了一个异步线程时,traceId 就会丢失。本方案,通过线程池中的装饰器,来将父线程中的traceId传递给子线程,从而保证 traceId 不丢失。
# 2 实现代码
/**
* redis 操作线程池
*/
@Bean(value = "redisOperatorThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor redisOperatorThreadPoolTaskExecutor() {
// todo 监听线程池使用的情况
return getThreadPoolTaskExecutor(redisOperatorThreadPoolProperties);
}
/**
* 根据线程池的属性值获取对应线程的设置
*
* @param baseThreadPoolProperties 线程池配置信息
* @return 线程池
*/
private ThreadPoolTaskExecutor getThreadPoolTaskExecutor(BaseThreadPoolProperties baseThreadPoolProperties) {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
if (StringUtils.isNotBlank(baseThreadPoolProperties.getThreadGroupName())) {
threadPoolTaskExecutor.setThreadGroupName(baseThreadPoolProperties.getThreadGroupName());
}
if (StringUtils.isNotBlank(baseThreadPoolProperties.getThreadNamePrefix())) {
threadPoolTaskExecutor.setThreadNamePrefix(baseThreadPoolProperties.getThreadNamePrefix());
}
if (null != baseThreadPoolProperties.getCorePoolSize() && baseThreadPoolProperties.getCorePoolSize() > 0) {
threadPoolTaskExecutor.setCorePoolSize(baseThreadPoolProperties.getCorePoolSize());
}
if (null != baseThreadPoolProperties.getQueueCapacity() && baseThreadPoolProperties.getQueueCapacity() > 0) {
threadPoolTaskExecutor.setQueueCapacity(baseThreadPoolProperties.getQueueCapacity());
}
if (null != baseThreadPoolProperties.getMaxPoolSize() && baseThreadPoolProperties.getMaxPoolSize() > 0) {
threadPoolTaskExecutor.setMaxPoolSize(baseThreadPoolProperties.getMaxPoolSize());
}
if (null != baseThreadPoolProperties.getKeepAliveSeconds() && baseThreadPoolProperties.getKeepAliveSeconds() > 0) {
threadPoolTaskExecutor.setKeepAliveSeconds(baseThreadPoolProperties.getKeepAliveSeconds());
}
// 设置拒绝策略
if (StringUtils.isNotBlank(baseThreadPoolProperties.getRejectedPolicy())) {
ThreadPoolRejectedPolicyEnum threadPoolRejectedPolicyEnum = ThreadPoolRejectedPolicyEnum.toEnum(baseThreadPoolProperties.getRejectedPolicy());
if (null != threadPoolRejectedPolicyEnum) {
threadPoolTaskExecutor.setRejectedExecutionHandler(threadPoolRejectedPolicyEnum.getRejectedExecutionHandler());
} else {
log.warn("Not exist rejectedPolicy:{}, return threadPoolRejectedPolicyEnum null.", baseThreadPoolProperties.getRejectedPolicy());
}
}
// 设置MdcRunnable
threadPoolTaskExecutor.setTaskDecorator(new MdcTaskDecorator());
return threadPoolTaskExecutor;
}
/**
* 主线程traceId传入到子线程(注意:线程池池化) 核心代码
*/
private static class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
String traceId = MDC.get(TRACE_ID);
return () -> {
try {
MDC.put(TRACE_ID, traceId);
runnable.run();
} finally {
MDC.remove(TRACE_ID);
}
};
}
}
# 3 效果
异步线程的traceId 和 主线程中的 traceId 一致,从而保证了业务在查看日志时,能够通过 traceId 完成串联一次完整请求的所有日志(包括异步线程中的日志)。
上次更新: 2023/03/22, 15:21:20