在spring中使用不同的创建线程池的方式调用对日志中traceId的生成逻辑的影响
- 使用问题:
用sleuth做日志追踪时,在用到@Async注解做异步时,发现traceId重新生成了,导致 无法和main线程关联到一起。
追踪源码后发现是因为sleuth对实现了AsyncConfigurer接口的线程池做包装时,由于加载顺序的问题导致未对线程池做代理,导致异步的线程重新生成了traceId。
场景描述:
一.不使用scheduling中的AsyncConfigurer创建线程池
1.使用JDK的ThreadPoolExecutor创建线程池(对应代理类LazyTraceExecutor)
ThreadPoolExecutor jdkExecutor = new ThreadPoolExecutor(5, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(60));
jdkExecutor.setThreadFactory(new CustomizableThreadFactory("jdkCustomExecutor-"));
jdkExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- 其中JDK的
executor
对应代理类LazyTraceExecutor
,该类只实现了executor的execute方法(对应runnable的代理类SpanContinuingTraceRunnable
),
所以其它调用方式(如submit)都会重新生成traceId。 - 使用executor.execute()调用的话 main线程和异步线程使用同一个traceId,不同的spanId。其他调用方式都会重新生成traceId。
因为sleuth-core源码中org.springframework.cloud.sleuth.instrument.async.AsyncDefaultAutoConfiguration
中方法executorBeanPostProcessor
会对线程池进行代理ExecutorBeanPostProcessor#postProcessAfterInitialization
,以支持traceId在异步线程中的输出。
图一
图二
2.使用spring的ThreadPoolTaskExecutor创建线程池(对应代理类LazyTraceThreadPoolTaskExecutor)
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor