1

我正在尝试让上下文传播在 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 存储库,其中包含演示应用程序的完整代码和重现问题的分步说明。

存在依赖性io.quarkus:quarkus-smallrye-context-propagation
Quarkus 版本:1.9.2

问:是我的代码问题还是 Quarkus 问题?

供参考:关于上下文传播的 Quarkus 文档

4

1 回答 1

4

您的代码本质上很好 [1],Quarkus 也很好——但有两点需要理解。

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

第二,上下文传播的关键是将线程本地状态从一个线程转移到另一个线程,但这不会自动发生。您可以通过调用相应的 API 自己(这将是“手动上下文传播”)执行此操作,或者您可以实现一个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";
    }
}

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

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

[1]如果您将其提交给您,则不必“上下文化”您Supplier的-自动执行此操作。ThreadContext.contextualSupplierManagedExecutorManagedExecutor

于 2020-11-27T16:22:42.480 回答