执行异步org.slf4j.MDC
时,父线程上下文以及MDC.put("fishid", randomId())
上下文丢失。
这很糟糕,因为我正在使用某种“鱼标记”来跟踪多个日志文件中的一个请求的日志。
CompletableFutures
问题:如何在List<CompletableFuture<UpdateHotelAllotmentsRsp>> futures =
tasks.stream()
.map(task -> CompletableFuture.supplyAsync(
() -> businesslogic(task))
.collect(Collectors.toList());
List results = futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
public void businesslogic(Task task) {
LOGGER.info("mdc fishtag context is lost here");
}
的任务期间保留该ID?
{{1}}
答案 0 :(得分:3)
最后,我创建了一个保留Supplier
的{{1}}包装器。如果有人有更好的想法随时发表评论。
MDC
答案 1 :(得分:0)
是的,Twitter Future正确地做到了这一点。他们有一个Future.scala知道的Local.scala类。
该修补程序适用于Java作者,以解决此问题,以便您的本地状态在所有使用CompletableFutures的库中传播。基本上,Future使用Local.scala并在内部使用ThreadLocal直到.thenApply或.thenAccept为止,它将捕获状态并将其在需要时不停地转移到下一个状态。这适用于所有具有零第三方库更改的第三方库。
这里还有更多,但请戳Java作者来修复他们的东西... http://mail.openjdk.java.net/pipermail/core-libs-dev/2017-May/047867.html
直到那时,MDC永远都不会通过第三方库工作。
我对此的帖子 Does CompletableFuture have a corresponding Local context?
答案 2 :(得分:0)
我解决此问题的最可读的方法如下-
----------------- Thread utils类--------------------
SELECT
DATE_FORMAT(created_at, '%M %Y') AS 'article',
DATE_FORMAT(created_at, '%m')AS 'm',
DATE_FORMAT(created_at,'%Y') AS 'y',
COUNT(id) AS 'total'
FROM posts
GROUP BY DATE_FORMAT(created_at, '%Y%M')
ORDER BY m DESC
---------------用法--------------
$archives = DB::select("SELECT DATE_FORMAT(created_at, '%M %Y') AS 'article',DATE_FORMAT(created_at, '%m')AS 'm', DATE_FORMAT(created_at,'%Y') AS 'y', COUNT(id) AS 'total' FROM posts GROUP BY DATE_FORMAT(created_at, '%Y%M') ORDER BY m DESC");`
ThreadUtils中的WithMdc必须重载以包含CompletableFuture接受的其他功能接口
请注意,withMdc()方法是静态导入的,以提高可读性。
答案 3 :(得分:0)
我的解决方案主题是(它将与JDK 9+一起使用,因为从该版本开始就公开了几个可重写的方法)
让完整的生态系统了解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
类看起来像
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 {
return 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
中的几个方法实例化自身版本,例如new CompletableFuture...
。对于此类方法(大多数公共静态方法),请使用替代方法来获取MDCAwareCompletableFuture
的实例。使用替代方法的一个例子可能是使用CompletableFuture.supplyAsync(...)
new MDCAwareCompletableFuture<>().completeAsync(...)
CompletableFuture
实例而陷入困境时,使用方法MDCAwareCompletableFuture
将getMDCAwareCompletionStage
的实例转换为CompletableFuture
到MDCAwareForkJoinPool
。显然,您无法在该库中保留上下文,但是在您的代码触及应用程序代码后,此方法仍将保留上下文。MDCAwareThreadPoolExecutor
。您也可以通过覆盖execute
方法来创建List<CompletableFuture<UpdateHotelAllotmentsRsp>> futures =
tasks.stream()
new MDCAwareCompletableFuture<UpdateHotelAllotmentsRsp>().completeAsync(
() -> businesslogic(task))
.collect(Collectors.toList());
List results = futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
public UpdateHotelAllotmentsRsp businesslogic(Task task) {
LOGGER.info("mdc fishtag context is not lost here");
}
,以服务您的用例。你明白了!有了它,您的代码就会像
ClinicMap.js
您可以在post中以相同的方式找到以上所有内容的详细说明。