被请求者应该如何处理请求超时?有可能吗?

How should an ask timeout be handled by the askee? Is it even possible?

假设我有一些客户端代码在 actor 上使用 akka 的 ask 模式:

implicit val timeout = Timeout(1.minute)
val result: Future[Any] = actor ? Question

演员是这样处理的:

def receive = {
  case Question =>
    // work work work
    // 3 minutes later...
    sender ! Answer
}

在这种情况下,result Future 预计会超时,因为回复将在三分钟后发送,但给定的超时仅为一分钟。

akka 的询问模式是否会采取任何措施来通知 "askee" 超时?有没有办法处理这个,例如取消演员在没有超时的情况下可能完成的任何剩余工作?

这不是内置的,但如果演员准备取消,您可以完成类似的事情。

在您的发件人中,您可以执行以下操作:

...
val actorForClosure = actor
future onFailure { case _ : AskTimeoutException => actorForClosure ! Cancel }

但是,演员必须能够处理取消。如果它阻塞了 3 分钟,那么取消请求直到计算完成后才会进入并且毫无意义。但是,如果您可以将计算分解为迭代回其自身的块,那么您可以在计算之间为 Cancel 留出间隙。因此,必须从一开始就考虑取消。

问题

所以你需要一种机制来停止在被询问者中进行的长时间 运行 计算,无论询问者是否超时。

一个解决方案

首先,只有被询问者才知道如何处理自己的计算。所以,只有它能优雅的阻止它。

处理此问题的常用方法是在消息中将 maximumTime 传递给被询问者,指示其发送完整答案的最长时间。

然后,在计算结果时,被请求者可以定期检查是否达到了最大时间,或者抛出一个 TimeoutException 或发送一个 Failure 给请求者:

def receive = {
  case MessageWithTimeout(msg, maximumTime) => compute(msg, maximumTime)
}

def compute(msg: Message, maximumTime: Long): T {
  val startTime = System.nanoTime()
  // ...
  // somewhere during the computation:
  if(System.nanoTime() - startTime > maximumTime) {
      throw new TimeoutException(maximumTime + "exceeded")
  }
  // ...
}

这样做,被问者将在maximumTime后停止计算。

如果你发送与asker相同的超时,那么很可能asker会在等待时超时,然后askee才会停止计算并且return.

需要注意的是,如果你抛出异常,那么演员的行为应该委托给监管者。