本文介紹了Quarkus純模式下的手動上下文傳播的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧!
問題描述
我正在嘗試使上下文傳播在Quarkus純模式下工作。
以下代碼在JVM模式下按預(yù)期工作,但在本機(jī)模式下返回MDC value: null
。
正如預(yù)期的那樣,我的意思是:
對curl http://localhost:8080/thread-context
的響應(yīng)是MDC value: from-thread-context
@Inject
ManagedExecutor managedExecutor;
@Inject
ThreadContext threadContext;
private final Supplier<String> mdcValueSupplier =
() -> "MDC value: " + MDC.get("foo") + "
";
@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();
}
我已經(jīng)創(chuàng)建了一個github repo,其中包含演示應(yīng)用的完整代碼和重現(xiàn)該問題的逐步說明。
存在依賴項io.quarkus:quarkus-smallrye-context-propagation
。
Quarkus版本:1.9.2
問:是我的代碼有問題,還是Quarkus有問題?
參考:Quarkus documentatin on context propagation
推薦答案
您的代碼基本上很好[1],Quarkus在這方面也很好–但有兩件事需要理解。
第一,您沒有執(zhí)行任何類型的手動上下文傳播。您的代碼是意外運(yùn)行的,因為Quarkus使用JBoss LogManager作為記錄器,并且它的MDC不是普通的ThreadLocal
,它是一個InheritableThreadLocal
。因此,它有時會傳播上下文本身。但這并不是可以依賴的。例如,如果您執(zhí)行實時重新加載(通過稍微修改代碼并再次運(yùn)行curl
),它也將停止在JVM模式下工作。
第二,上下文傳播的要點(diǎn)是將線程本地狀態(tài)從一個線程轉(zhuǎn)移到另一個線程,但這不是自動發(fā)生的。您可以通過調(diào)用相應(yīng)的API自己執(zhí)行該操作(即手動上下文傳播),也可以實現(xiàn)ThreadContextProvider
。
我簡要介紹了MDC API(http://www.slf4j.org/api/org/slf4j/MDC.html),似乎可以使用getCopyOfContextMap
和setContextMap
實現(xiàn)基本的上下文傳播。下面是我快速組裝的一個實現(xiàn)–注意,我沒有對代碼進(jìn)行太多測試:
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";
}
}
如果您創(chuàng)建的META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider
文件包含此類的完全限定名稱,則MDC傳播應(yīng)該適用于您,即使是在本機(jī)中也是如此。
這樣做可能存在的一個問題是,無論您在新線程上對MDC
所做的任何更改都不會傳播回原始線程,因為SLF4J故意不提供對后備映射的訪問,它只分發(fā)副本。這對你來說可能沒問題,也可能不好。
[1]如果您將ManagedExecutor
提交給ManagedExecutor
,則Supplier
您的Supplier
不必將Supplier
提交給ManagedExecutor
,ManagedExecutor
會自動執(zhí)行此操作。
這篇關(guān)于Quarkus純模式下的手動上下文傳播的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,