【问题标题】:debugging scala futures - how to determine the future's execution context调试 Scala 期货 - 如何确定未来的执行上下文
【发布时间】:2016-02-16 16:24:00
【问题描述】:

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

【问题讨论】:

  • 您可以通过将令牌放入DynamicVariable 来避免传递令牌。你可以有类似case class Context(requestId: Int, /* other things you need to pass around */); object Context { val context = new DynamicVariable(Context(0 /* must be some default value or e.g. null at this point */, ...)) }
  • @Kolmar:好主意——为什么不把它作为答案呢? PS - 任何建议如何隐式传递?
  • 好吧,我已经回答了关于隐式传递 requestId 的问题,但我真的不知道传递堆栈跟踪有多大用处。
  • 谢谢!非常详细的答案。传递堆栈跟踪可能是一种矫枉过正,但我​​认为至少有一些允许对整个事件流的日志进行 grepping 的令牌是必须的.​​.....如果它是事务明确需要的(用户 ID 等) -伟大的。如果您没有,我认为添加它是有益的。

标签: scala debugging promise future


【解决方案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 */)
  }
} 

缺点是

  • 在处理的深处并不能立即清楚地知道有一些上下文可用,它有什么内容以及引用透明度被破坏了。我认为最好严格限制可用DynamicVariables 的数量,最好不要超过 1 个或最多 2 个并记录它们的使用情况。
  • 上下文的所有内容必须具有默认值或nulls,或者默认情况下它本身必须是null (new DynamicVariable[Context](null))。在处理之前忘记初始化Context 或其内容可能会导致严重错误。

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


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

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

【讨论】:

    猜你喜欢
    • 2014-08-04
    • 2016-07-11
    • 2018-08-04
    • 1970-01-01
    • 2015-09-30
    • 2019-11-04
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多