2 回答

TA貢獻1829條經驗 獲得超9個贊
我的解決方案主題是(它可以與 JDK 9+ 一起使用,因為自該版本以來公開了幾個可覆蓋的方法)
讓整個生態系統了解 MDC
為此,我們需要解決以下場景:
我們什么時候從這個類中獲得 CompletableFuture 的新實例?→ 我們需要返回相同的 MDC 感知版本。
我們什么時候從這個類之外獲得 CompletableFuture 的新實例?→ 我們需要返回相同的 MDC 感知版本。
在 CompletableFuture 類中使用哪個執行器?→ 在任何情況下,我們都需要確保所有執行者都了解 MDC
為此,讓我們CompletableFuture
通過擴展它來創建一個 MDC 感知版本類。我的版本如下所示
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Supplier;
public class MDCAwareCompletableFuture<T> extends CompletableFuture<T> {
public static final ExecutorService MDC_AWARE_ASYNC_POOL = new MDCAwareForkJoinPool();
@Override
public CompletableFuture newIncompleteFuture() {
return new MDCAwareCompletableFuture();
}
@Override
public Executor defaultExecutor() {
return MDC_AWARE_ASYNC_POOL;
}
public static <T> CompletionStage<T> getMDCAwareCompletionStage(CompletableFuture<T> future) {
return new MDCAwareCompletableFuture<>()
.completeAsync(() -> null)
.thenCombineAsync(future, (aVoid, value) -> value);
}
public static <T> CompletionStage<T> getMDCHandledCompletionStage(CompletableFuture<T> future,
Function<Throwable, T> throwableFunction) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return getMDCAwareCompletionStage(future)
.handle((value, throwable) -> {
setMDCContext(contextMap);
if (throwable != null) {
return throwableFunction.apply(throwable);
}
return value;
});
}
}
該類MDCAwareForkJoinPool看起來像(ForkJoinTask為簡單起見,跳過了帶參數的方法)
public class MDCAwareForkJoinPool extends ForkJoinPool {
//Override constructors which you need
@Override
public <T> ForkJoinTask<T> submit(Callable<T> task) {
return super.submit(MDCUtility.wrapWithMdcContext(task));
}
@Override
public <T> ForkJoinTask<T> submit(Runnable task, T result) {
return super.submit(wrapWithMdcContext(task), result);
}
@Override
public ForkJoinTask<?> submit(Runnable task) {
return super.submit(wrapWithMdcContext(task));
}
@Override
public void execute(Runnable task) {
super.execute(wrapWithMdcContext(task));
}
}
包裝的實用方法如下
public static <T> Callable<T> wrapWithMdcContext(Callable<T> task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
return task.call();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static Runnable wrapWithMdcContext(Runnable task) {
//save the current MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
setMDCContext(contextMap);
try {
task.run();
} finally {
// once the task is complete, clear MDC
MDC.clear();
}
};
}
public static void setMDCContext(Map<String, String> contextMap) {
MDC.clear();
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
}
以下是一些使用指南:
使用類
MDCAwareCompletableFuture
而不是類CompletableFuture
。類中的幾個方法
CompletableFuture
實例化了 self 版本,例如new CompletableFuture...
. 對于此類方法(大多數公共靜態方法),請使用替代方法來獲取MDCAwareCompletableFuture
. 使用替代方法的示例可能是,而不是使用CompletableFuture.supplyAsync(...)
,您可以選擇new MDCAwareCompletableFuture<>().completeAsync(...)
當您因為某個外部庫返回
CompletableFuture
一個. 顯然,您不能在該庫中保留上下文,但是在您的代碼命中應用程序代碼后,此方法仍會保留上下文。MDCAwareCompletableFuture
getMDCAwareCompletionStage
CompletableFuture
在提供 executor 作為參數時,請確保它是 MDC Aware,例如
MDCAwareForkJoinPool
. 您也可以MDCAwareThreadPoolExecutor
通過覆蓋execute
方法創建以服務于您的用例。你明白了!
您可以在一篇關于相同內容的帖子中找到上述所有內容的詳細說明。
這樣,您的代碼可能看起來像
new MDCAwareCompletableFuture<>().completeAsync(() -> { getAcountDetails(user); return null; });

TA貢獻1808條經驗 獲得超4個贊
創建包裝方法
static CompletableFuture<Void> myMethod(Runnable runnable) {
Map<String, String> previous = MDC.getCopyOfContextMap();
return CompletableFuture.runAsync(() -> {
MDC.setContextMap(previous);
try {
runnable.run();
} finally {
MDC.clear();
}
});
}
并使用它代替CompletableFuture.runAsync.
添加回答
舉報