【问题标题】:Manual context propagation in Quarkus native modeQuarkus 原生模式下的手动上下文传播
【发布时间】:2020-11-27 14:29:25
【问题描述】:

我正在尝试让上下文传播在 Quarkus 本机模式下工作。
下面的代码在 JVM 模式下按预期工作,但在本机模式下返回 MDC value: null
“按预期”是指:
回复curl http://localhost:8080/thread-contextMDC 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 问题?

供参考:Quarkus documentatin on context propagation

【问题讨论】:

    标签: java quarkus graalvm smallrye


    【解决方案1】:

    您的代码基本上没问题 [1],Quarkus 在这方面也不错 - 但有两点需要理解。

    第一,您没有进行任何类型的“手动上下文传播”。您的代码是偶然运行的,因为 Quarkus 使用 JBoss LogManager 作为记录器,并且它的 MDC 不是普通的ThreadLocal,而是InheritableThreadLocal。所以它有时会传播上下文本身。但这没有什么可依赖的。例如,如果您进行实时重新加载(通过稍微修改代码并再次运行 curl),它也会停止在 JVM 模式下工作。

    第二,上下文传播的关键是将线程本地状态从一个线程转移到另一个线程,但这不会自动发生。您可以通过调用各自的 API 自己(即“手动上下文传播”)执行此操作,也可以实现 ThreadContextProvider

    我简要了解了 MDC API (http://www.slf4j.org/api/org/slf4j/MDC.html),似乎可以使用 getCopyOfContextMapsetContextMap 实现基本的上下文传播。这是我快速整理的一个实现——请注意,我没有过多地测试代码:

    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";
        }
    }
    

    如果您创建一个包含此类的完全限定名称的 META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider 文件,那么 MDC 传播应该适合您,即使在本机中也是如此。

    一个可能的问题是,你在新线程上对MDC 所做的任何更改都不会传播回原始线程,因为 SLF4J 故意不提供对支持映射的访问,它只分发副本.这对你来说可能没问题。

    [1] 如果您将 Supplier 提交给 ManagedExecutor,则您不必通过 ThreadContext.contextualSupplier 将其“上下文化”——ManagedExecutor 会自动执行此操作。

    【讨论】:

    • 感谢您非常详细的回复!我已经从 Quarkus 文档(问题中的链接)中“复制粘贴”了上下文化和 ManagedExecutor 用法,但对它们的工作方式没有深入了解。你能说明一下文档中这两个东西是一起使用的吗?
    • 第二个想法。但这在某种程度上适用于“Uni”(检查 github 上的完整代码,请参阅我的问题中的链接),无需执行任何其他操作。
    猜你喜欢
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    • 2020-07-18
    • 2018-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多