Quarkus 本机模式下的手动上下文传播
Manual context propagation in Quarkus native mode
我正在尝试让上下文传播在 Quarkus 本机模式下工作。
下面的代码在 JVM 模式下按预期工作,但在本机模式下 returns MDC value: null
。
“按预期”我的意思是:
对 curl http://localhost:8080/thread-context
的响应是 MDC value: from-thread-context
@Inject
ManagedExecutor managedExecutor;
@Inject
ThreadContext threadContext;
private final Supplier<String> mdcValueSupplier =
() -> "MDC value: " + MDC.get("foo") + "\n";
@GET
@Path("thread-context")
public String get() throws ExecutionException, InterruptedException {
MDC.put("foo", "from-thread-context");
Supplier<String> ctxSupplier = threadContext.contextualSupplier(mdcValueSupplier);
return managedExecutor.supplyAsync(ctxSupplier).get();
}
我创建了一个 github repo,其中包含演示应用程序的完整代码和重现该问题的分步说明。
依赖项 io.quarkus:quarkus-smallrye-context-propagation
存在。
Quarkus 版本:1.9.2
问:是我的代码有问题,还是 Quarkus 有问题?
您的代码基本上没问题 [1],Quarkus 在这方面也很好——但有两点需要理解。
第一,您没有进行任何类型的“手动上下文传播”。您的代码是偶然运行的,因为 Quarkus 使用 JBoss LogManager 作为记录器,并且它的 MDC 不是普通的 ThreadLocal
,而是 InheritableThreadLocal
。所以它有时有点传播上下文本身。但这没什么可依赖的。例如,如果您进行实时重新加载(通过稍微修改代码并再次 运行 curl
),它也会在 JVM 模式下停止工作。
第二,上下文传播的关键是将线程本地状态从一个线程传输到另一个线程,但这不会自动发生。要么你自己做(那将是“手动上下文传播”),通过调用相应的 APIs,或者你可以实现一个 ThreadContextProvider
.
我简要查看了 MDC API (http://www.slf4j.org/api/org/slf4j/MDC.html),似乎可以使用 getCopyOfContextMap
和 setContextMap
实现基本的上下文传播。这是我快速组合的一个实现——注意,我没有对代码进行太多测试:
import org.eclipse.microprofile.context.spi.ThreadContextProvider;
import org.eclipse.microprofile.context.spi.ThreadContextSnapshot;
import org.slf4j.MDC;
import java.util.Map;
public class MdcContextProvider implements ThreadContextProvider {
@Override
public ThreadContextSnapshot currentContext(Map<String, String> props) {
Map<String, String> propagate = MDC.getCopyOfContextMap();
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.setContextMap(propagate);
return () -> {
MDC.setContextMap(old);
};
};
}
@Override
public ThreadContextSnapshot clearedContext(Map<String, String> props) {
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.clear();
return () -> {
MDC.setContextMap(old);
};
};
}
@Override
public String getThreadContextType() {
return "SLF4J MDC";
}
}
如果您创建一个包含此 class 的完全限定名称的 META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider
文件,那么 MDC 传播应该适合您,即使是在本机中也是如此。
一个可能的问题是,无论您在新线程上对 MDC
所做的任何更改都不会传播回原始线程,因为 SLF4J 有意不提供对支持映射的访问,它只分发副本。这可能适合你,也可能不适合。
[1] 如果您将 ManagedExecutor
提交给 ManagedExecutor
,则不必通过 ThreadContext.contextualSupplier
将您的 Supplier
“上下文化”——ManagedExecutor
会自动执行此操作.
我正在尝试让上下文传播在 Quarkus 本机模式下工作。
下面的代码在 JVM 模式下按预期工作,但在本机模式下 returns MDC value: null
。
“按预期”我的意思是:
对 curl http://localhost:8080/thread-context
的响应是 MDC value: from-thread-context
@Inject
ManagedExecutor managedExecutor;
@Inject
ThreadContext threadContext;
private final Supplier<String> mdcValueSupplier =
() -> "MDC value: " + MDC.get("foo") + "\n";
@GET
@Path("thread-context")
public String get() throws ExecutionException, InterruptedException {
MDC.put("foo", "from-thread-context");
Supplier<String> ctxSupplier = threadContext.contextualSupplier(mdcValueSupplier);
return managedExecutor.supplyAsync(ctxSupplier).get();
}
我创建了一个 github repo,其中包含演示应用程序的完整代码和重现该问题的分步说明。
依赖项 io.quarkus:quarkus-smallrye-context-propagation
存在。
Quarkus 版本:1.9.2
问:是我的代码有问题,还是 Quarkus 有问题?
您的代码基本上没问题 [1],Quarkus 在这方面也很好——但有两点需要理解。
第一,您没有进行任何类型的“手动上下文传播”。您的代码是偶然运行的,因为 Quarkus 使用 JBoss LogManager 作为记录器,并且它的 MDC 不是普通的 ThreadLocal
,而是 InheritableThreadLocal
。所以它有时有点传播上下文本身。但这没什么可依赖的。例如,如果您进行实时重新加载(通过稍微修改代码并再次 运行 curl
),它也会在 JVM 模式下停止工作。
第二,上下文传播的关键是将线程本地状态从一个线程传输到另一个线程,但这不会自动发生。要么你自己做(那将是“手动上下文传播”),通过调用相应的 APIs,或者你可以实现一个 ThreadContextProvider
.
我简要查看了 MDC API (http://www.slf4j.org/api/org/slf4j/MDC.html),似乎可以使用 getCopyOfContextMap
和 setContextMap
实现基本的上下文传播。这是我快速组合的一个实现——注意,我没有对代码进行太多测试:
import org.eclipse.microprofile.context.spi.ThreadContextProvider;
import org.eclipse.microprofile.context.spi.ThreadContextSnapshot;
import org.slf4j.MDC;
import java.util.Map;
public class MdcContextProvider implements ThreadContextProvider {
@Override
public ThreadContextSnapshot currentContext(Map<String, String> props) {
Map<String, String> propagate = MDC.getCopyOfContextMap();
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.setContextMap(propagate);
return () -> {
MDC.setContextMap(old);
};
};
}
@Override
public ThreadContextSnapshot clearedContext(Map<String, String> props) {
return () -> {
Map<String, String> old = MDC.getCopyOfContextMap();
MDC.clear();
return () -> {
MDC.setContextMap(old);
};
};
}
@Override
public String getThreadContextType() {
return "SLF4J MDC";
}
}
如果您创建一个包含此 class 的完全限定名称的 META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider
文件,那么 MDC 传播应该适合您,即使是在本机中也是如此。
一个可能的问题是,无论您在新线程上对 MDC
所做的任何更改都不会传播回原始线程,因为 SLF4J 有意不提供对支持映射的访问,它只分发副本。这可能适合你,也可能不适合。
[1] 如果您将 ManagedExecutor
提交给 ManagedExecutor
,则不必通过 ThreadContext.contextualSupplier
将您的 Supplier
“上下文化”——ManagedExecutor
会自动执行此操作.