解决代码使用CompletableFuture做异步时spring-cloud-starter-sleuth的日志追踪号为空的情况

产生问题原因

就是异步调用,导致spanId和traceId丢失了
@Async 注解的异步调用是没问题的

前提

使用spring-cloud-starter-sleuth jar包版本2.2.8.RELEASE
关于追踪号的xml 配置为

<pattern>%yellow(%date{yyyy-MM-dd HH:mm:ss.SSS}) [%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-B3-ParentSpanId:-},%X{X-Span-Export:-}] %highlight(%-5level) %yellow(%thread) %green(%logger:%line)  %msg%n</pattern>
 <pattern>%yellow(%date{yyyy-MM-dd HH:mm:ss.SSS}) [%X{traceId:-},%X{spanId:-},%X{parentId:-},%X{spanExportable:false}] %highlight(%-5level) %yellow(%thread) %green(%logger:%line)  %msg%n</pattern>

使用自定义线程池+MDC解决

线程池也可以用这中方法解决
1、自定义线程池

import org.slf4j.MDC;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class CustomMdcThreadPoolWrapper extends ThreadPoolExecutor {

    public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                      BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                      BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public CustomMdcThreadPoolWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                      BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public CustomMdcThreadPoolWrapper(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()));
    }
import org.slf4j.MDC;

import java.util.Map;
import java.util.concurrent.Callable;

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();
            }
        };
    }
}

2、使用

public String testMdc() {
    log.info("测试自定义线程池");
    CompletableFuture.runAsync(() ->
                    print("zhangsan")
            , customMdcThreadPoolWrapper);
    CompletableFuture.runAsync(() ->
                    print("lisi")
            , customMdcThreadPoolWrapper);
    return "testMdc ok";
}

当CompletableFuture有一个任务时,代码的写法

import brave.Span;
import brave.Tracer;

@Autowired
private Tracer tracer;

public String completableFutureTestOne() {
    log.info("--------");
    Span span = tracer.nextSpan();
    CompletableFuture.supplyAsync(()->{
        try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
            print("zhangsan");
            return "zhangsan";
        }catch (Exception e) {
            e.printStackTrace();
            return "zhangsan";
        }

    });
    return "ok111";
}

当CompletableFuture有多个任务时,代码的写法

import brave.Span;
import brave.Tracer;
import brave.propagation.TraceContext;

@Autowired
private Tracer tracer;

public String completableFutureTest() {
    log.info("--------");
    TraceContext context = tracer.currentSpan().context();
    // 也可以按照下一行获取TraceContext
    // TraceContext context = tracer.nextSpan().context();
    CompletableFuture.supplyAsync(()->{
        Span span = tracer.newChild(context);
        try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
            print("zhangsan");
            return "zhangsan";
        }catch (Exception e) {
            e.printStackTrace();
            return "zhangsan";
        }

    });

    CompletableFuture.supplyAsync(()->{
        Span span = tracer.newChild(context);
        try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
            print("lisi");
            return "lisi";
        }catch (Exception e) {
            e.printStackTrace();
            return "lisi";
        }
    });
    return "ok";
}

posted @ 2023-09-05 15:07  品书读茶  阅读(584)  评论(0编辑  收藏  举报