与我发布的另一个问题相关(scala futures - keeping track of request context when threadId is irrelevant) 在调试未来时,调用堆栈信息量不大(因为调用上下文通常在另一个线程中,另一个时间)。 当存在导致相同的未来代码的不同路径时(例如,从代码中的许多地方调用的DAO的使用等),这尤其成问题。 你知道一个优雅的解决方案吗? 我正在考虑传递令牌/请求ID(对于由Web服务器请求启动的流) - 但这需要传递它 - 并且还不包括您可以在堆栈跟踪中看到的任何状态。 也许绕过一堆? :)
答案 0 :(得分:1)
假设你上课
case class Context(requestId: Int, /* other things you need to pass around */)
隐式发送它有两种基本方法:
1)向任何需要它的函数添加一个隐式Context
参数:
def processInAnotherThread(/* explicit arguments */)(
implicit evaluationContext: scala.concurrent.EvaluationContext,
context: Context): Future[Result] = ???
def processRequest = {
/* ... */
implicit val context: Context = Context(getRequestId, /* ... */)
processInAnotherThread(/* explicit parameters */)
}
缺点是需要访问Context
的每个函数都必须具有此参数,并且它会使函数签名相当多。
2)将其放入DynamicVariable
:
// Context companion object
object Context {
val context: DynamicVariable[Context] =
new DynamicVariable[Context](Context(0, /* ... */))
}
def processInAnotherThread(/* explicit arguments */)(
implicit evaluationContext: scala.concurrent.EvaluationContext
): Future[Result] = {
// get requestId from context
Context.context.value.requestId
/* ... */
}
def processRequest = {
/* ... */
Context.context.withValue(Context(getRequestId, /* ... */)) {
processInAnotherThread(/* explicit parameters */)
}
}
缺点是
DynamicVariable
的数量,最好不要超过1或最多2,并记录其使用情况。null
s用于其所有内容,或者默认情况下它必须是null
(new DynamicVariable[Context](null)
)。忘记在处理之前初始化Context
或其内容可能会导致令人讨厌的错误。 DynamicVariable
仍然比某些全局变量好得多,并且不会影响不以任何方式直接使用它的函数的签名。
在这两种情况下,您都可以使用Context
copy
方法更新现有case class
的内容。例如:
def deepInProcessing(/* ... */): Future[Result] =
Context.context.withValue(
Context.context.value.copy(someParameter = newParameterValue)
) {
processFurther(/* ... */)
}