SpringBoot中使用链路追踪时,由于多线程之间的切换、或者使用的不是Spring提供的线程池时会发生traceId丢失。

处理方式一

线程池使用的是Spring提供的:ThreadPoolTaskExecutor,可以通过实现TaskDecorator解决

@Bean
public ThreadPoolTaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(4);
    taskExecutor.setMaxPoolSize(8);
    taskExecutor.setQueueCapacity(100);
    taskExecutor.setThreadNamePrefix("taskExecutor-");
    taskExecutor.setKeepAliveSeconds(30);
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    taskExecutor.setTaskDecorator(runnable -> () -> {
        try {
            // 获取当前线程的 MDC 上下文
            Map<String, String> contextMap = MDC.getCopyOfContextMap();
            // 将 MDC 上下文传递到新线程
            if (Objects.nonNull(contextMap)) {
                MDC.setContextMap(contextMap);
            }
            // 执行任务
            runnable.run();
        } finally {
            // 清理 MDC 上下文,避免内存泄漏
            MDC.clear();
        }
    });
    taskExecutor.initialize();
    return taskExecutor;
}

处理方式二

直接手动创建一个链路,然后把traceId放入log的MDC上下文中

// 注入trace依赖
@Resource
private Tracer tracer;

// 示例片段
Span span = tracer.spanBuilder().start();
try {
    TraceContext context = span.context();
    MDC.put("traceId", context.traceId());
    MDC.put("spanId", context.spanId());
} finally {
    span.end();
    MDC.remove("traceId");
    MDC.remove("spanId");
}

处理方式三

如果是有上级链路,需要当前线程继续传递下去,可以使用CurrentTraceContext的wrap方法

// 注入依赖
@Resource
private Tracer tracer;

// 代码片段
CurrentTraceContext traceContext = tracer.currentTraceContext();

// 根据需要使用对于的包装方法
<C> Callable<C> wrap(Callable<C> var1);

Runnable wrap(Runnable var1);

Executor wrap(Executor var1);

ExecutorService wrap(ExecutorService var1);

更多推荐