阿卡等待一群演员回应的好方法是什么?

时间:2015-01-31 19:17:07

标签: scala akka future

在Akka,我想发出一个"状态"消息给群集中的actor以获取其状态。这些行为者可能是各种健康状态,包括死亡/无法回应。

我想等一段时间,比如说10秒,然后继续我在那个时间限制内收到的任何结果。我不想让整个事情失败,因为1或2有问题,并且没有在10秒内做出响应/超时。

我试过这个:

object GetStats {
  def unapply(n: ActorRef )(implicit system: ActorSystem): Option[Future[Any]] = Try {
    implicit val t: Timeout = Timeout(10 seconds)
    n ? "A" 
  }.toOption
}
...
val z = List(a,b,c,d)  // where a-d are ActorRefs to nodes I want to status
val q = z.collect {
   case GetStats(s) => s
}
// OK, so here 'q' is a List[Future[Any]]
val allInverted = Future.sequence(q) // now we have Future[List[Any]]
val ok =  Await.result(allInverted, 10 seconds).asInstanceOf[List[String]]
println(ok)

此代码的问题在于,如果一个或多个不响应,它似乎会抛出TimeoutException。然后,我无法得到回复的回复。

1 个答案:

答案 0 :(得分:4)

假设您确实需要每10秒至少收集一次部分统计信息 - 解决方案是转换"不响应"实际失败。

要实现这一点,只需稍微增加Await超时,与implicit val t:Timeout进行比较即可。之后你的期货(从?返回)将会提前失败。所以你可以recover他们:

// Works only when AskTimeout >> AwaitTimeout
val qfiltered = q.map(_.map(Some(_)).recover{case _ => None}) //it's better to match TimeoutException here instead of `_`
val allInverted = Future.sequence(q).map(_.flatten)

示例:

scala> class MyActor extends Actor{ def receive = {case 1 => sender ! 2; case _ =>}}
defined class MyActor

scala> val a = sys.actorOf(Props[MyActor])
a: akka.actor.ActorRef = Actor[akka://1/user/$c#1361310022]

scala> implicit val t: Timeout = Timeout(1 seconds)
t: akka.util.Timeout = Timeout(1 second)

scala> val l = List(a ? 1, a ? 100500).map(_.map(Some(_)).recover{case _ => None})
l: List[scala.concurrent.Future[Option[Any]]] = List(scala.concurrent.impl.Promise$DefaultPromise@7faaa183, scala.concurrent.impl.Promise$DefaultPromise@1b51e0f0)

scala> Await.result(Future.sequence(l).map(_.flatten), 3 seconds)
warning: there were 1 feature warning(s); re-run with -feature for details
res29: List[Any] = List(2)

如果您想知道哪些未来没有回应 - 请删除flatten

接收部分响应应该足以连续收集统计数据,好像某个工作人员没有及时响应 - 它将在下一次回复实际数据并且不会丢失任何数据。但是你应该正确处理工作人员的生命周期,而不是松散(如果重要的话)演员本身内部的任何数据。

如果超时的原因只是系统的高压 - 你可以考虑:

  • 工作人员独立池
  • 背压
  • 缓存输入请求(系统超载时)。

如果这种超时的原因是某些远程存储 - 那么部分响应是正确的方法来处理它,如果客户端已经准备好了。例如,WebUI可以警告用户使用某些旋转的东西显示的数据可能不充分。但是不要忘记不阻止具有存储请求的actor(期货可能有帮助)或者至少将它们移动到separrate线程池。

如果工作人员因失败而没有做出回应(例外) - 您仍然可以通过preRestart向发件人发送通知 - 这样您也可以收到工作人员没有统计数据的原因。这里唯一的事情是 - 你应该检查发件人是否可用(it may not be

P.S。我希望你不要在某个演员身上做Await.result - 不建议至少为你的应用程序性能阻止演员。在某些情况下,它甚至可能导致死锁或内存泄漏。所以等待应该放在系统的外观中(如果底层框架不支持期货)。

所以可能有意义地异步处理你的答案(如果一些演员没有回应,你仍然需要从失败中恢复它们):

 //actor:
 val parent = sender
 for(list <- Future.sequence(qfiltered)) {
     parent ! process(list)
 }

 //in sender (outside of the actors):
 Await(actor ? Get, 10 seconds)
相关问题