调试scala期货 - 如何确定未来的执行环境

时间:2015-11-15 14:15:26

标签: scala debugging promise future

与我发布的另一个问题相关(scala futures - keeping track of request context when threadId is irrelevant) 在调试未来时,调用堆栈信息量不大(因为调用上下文通常在另一个线程中,另一个时间)。 当存在导致相同的未来代码的不同路径时(例如,从代码中的许多地方调用的DAO的使用等),这尤其成问题。 你知道一个优雅的解决方案吗? 我正在考虑传递令牌/请求ID(对于由Web服务器请求启动的流) - 但这需要传递它 - 并且还不包括您可以在堆栈跟踪中看到的任何状态。 也许绕过一堆? :)

1 个答案:

答案 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用于其所有内容,或者默认情况下它必须是nullnew DynamicVariable[Context](null))。忘记在处理之前初始化Context或其内容可能会导致令人讨厌的错误。

DynamicVariable仍然比某些全局变量好得多,并且不会影响不以任何方式直接使用它的函数的签名。

在这两种情况下,您都可以使用Context copy方法更新现有case class的内容。例如:

def deepInProcessing(/* ... */): Future[Result] =
  Context.context.withValue(
    Context.context.value.copy(someParameter = newParameterValue)
  ) {
    processFurther(/* ... */)
  }