我目前正在向一个项目添加分布式跟踪。但是,跟踪范围使用线程局部变量来存储跟踪上下文,例如。 traceId
或者 spanId
. 因此,无论何时创建 CompletableFuture
您需要用一个“可跟踪”的执行器来 Package 它的执行器,该执行器将这些变量传递给新线程。
Spring Boot提供了这样的 Package 。
以上链接中的建议如下:
CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(() -> {
// perform some logic
return 1_000_000L;
}, new TraceableExecutorService(beanFactory, executorService,
// 'calculateTax' explicitly names the span - this param is optional
"calculateTax"));
我想避免创建一个新的 TraceableExecutorService
每 CompletableFuture
我创造。主要原因是1)这会造成大量代码重复,2)我需要注入 BeanFactory
如果有人忘记添加 Package 器,跟踪就会中断。
到目前为止,我已经找到了三种解决方案,但并不理想:
使用此上下文传播库。虽然这可能有效(几个月前我测试了这个库,不记得我是否成功地使它工作了),但它仍然受到来自上面的3的影响。你需要记住导入 nl.talsmasoftware.context.futures.ContextAwareCompletableFuture
而不是典型的java CompletableFuture
,否则跟踪将不起作用。
创建一个小 Package 。这也有同样的问题。如果忘记使用 Package 器,跟踪将停止工作。
向项目中添加一个静态代码分析工具,检查您是否使用过典型的java CompletableFuture#supplyAsync
.
我做的 Package 是这样的:
@Service
public class TraceAwareCompletableFutureFactory implements CompletableFutureFactory {
private final BeanFactory beanFactory;
private final Executor defaultExecutor;
public TraceAwareCompletableFutureFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.defaultExecutor = wrap(ForkJoinPool.commonPool());
}
@Override
public <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier) {
return supplyAsync(supplier, defaultExecutor);
}
@Override
public <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier, Executor executor) {
return CompletableFuture.supplyAsync(supplier, wrap(executor));
}
private Executor wrap(Executor executor) {
return new LazyTraceExecutor(beanFactory, executor);
}
}
我一直在想,解决这三个问题的一个办法就是以某种方式违约 CompletableFuture#supplyAsync
使用不同的默认执行器。在看 CompletableFuture
的代码,这是不可能的。 supplyAsync
是静态方法并获取其默认值 Executor
从 private static
变量。
有没有办法做这样的事?你能提出一个替代我的解决方案吗?
暂无答案!
目前还没有任何答案,快来回答吧!