我想问一下为什么MDCContext在调用Mono的awaitSingle后变空了
这是代码片段
suspend fun getTemplates(request: GetPaginatedTemplateServiceRequest): PaginationTemplates {
var context = MDCContext()
val count = templateRepository.findByName(
request.templateName
).awaitSingle()
context = MDCContext()
}
您的信息模板存储库正在使用 ReactiveMongo。
第一次调用 MDCContext,我在该函数之前生成的上下文映射中有一些
x-request-id
但是在我用 Mono 调用awaitSingle之后,MDCContext 变空了(这是我不期望的)
我尝试的一个解决方案是用 withContext 函数包装awaitSingle
下面的例子
var context = MDCContext()
val count = withContext(Dispatchers.IO.plus(MDCContext())) {
templateRepository.findTemplatesCount(
request.templateName
).awaitSingle()
}
context = MDCContext()
然后 mdc 上下文的第二次调用仍然包含像第一次调用一样的项目(这是我所期望的)。
我实际上可以使用该解决方案,但是已经有很多代码像这样使用awaitSingle。有什么简单的解决方案吗?可能就像自动将上下文传递给单声道或其他东西?
谢谢,如果不够清楚请告诉我
MDCContext
需要跨异步边界进行显式管理。不幸的是,ReactiveMongo
本身并不处理此上下文的传播。
没有一种自动方法将上下文传递给每个
Mono
操作,除非反应式库明确支持此上下文传播。
一些反应式框架可能会自动传播上下文,而其他框架可能不会。
本质上,使用
withContext
的当前解决方案是维持跨awaitSingle()
调用上下文的最实用的方法。继续使用已经实现的
withContext
方法来确保上下文传播;并考虑将其封装到可重用的实用程序中,包装需要上下文的异步操作。以下是封装上下文传播逻辑的草稿示例。
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.slf4j.MDC
import reactor.core.publisher.Mono
suspend fun <T> withMdcContext(block: suspend () -> T): T {
val contextMap = MDC.getCopyOfContextMap() ?: emptyMap()
return withContext(Dispatchers.IO + MDCContext(contextMap)) {
block()
}
}
// Extension function for context preservation
fun <T> Mono<T>.preserveContext(): Mono<T> =
this.subscriberContext { context ->
val mdcContext = context.get(MDCContext::class.java)
context + (mdcContext ?: MDCContext())
}
调用 preserveContext()
显式确保在反应操作之前设置的任何上下文都保留在整个反应链中。 在上下文传播的默认行为在各种反应式库之间可能不一致的情况下,这是保护上下文完整性的主动步骤。
suspend fun getTemplates(request: GetPaginatedTemplateServiceRequest): PaginationTemplates {
val count = withMdcContext {
templateRepository.findByName(request.templateName)
.preserveContext() // Ensure context preservation in Mono
.awaitSingle()
}
// ...
}