Akka RoundRobinPool与OneForOneStrategy重新启动

时间:2017-11-10 08:51:12

标签: scala akka

我有一个简单的“hello world”actor系统,其中包含RoundRobinPool路由器和OneForOneStrategy重启策略。我希望以下代码最终(在几次失败之后)打印10条"work done"消息,但实际上似乎失败的Worker个actor没有重新启动:

import java.time.LocalDateTime

import akka.actor.SupervisorStrategy._
import akka.actor.{Actor, ActorSystem, OneForOneStrategy, Props}
import akka.routing.{RoundRobinPool, RouterConfig}

import scala.concurrent.duration._

/**
  * Worker Actor
  */
class Worker extends Actor {

  override def receive: Receive = {
    case DoIt => doit
  }

  def doit = {
    Thread.sleep(900)
    val dt = LocalDateTime.now().getSecond
    if ((dt % 2) == 0) throw new Exception("Error")
    else println("work done")
  }
}

/**
  * Message
  */
case object DoIt

/**
  * Entry point
  */
object PoolHelloWorld {

  def main(args: Array[String]): Unit = {
    val system = ActorSystem("PoolSystem")

    val supervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 20, withinTimeRange = 60 seconds) {
        case _: Exception => Restart
      }
    val routerConfig: RouterConfig =
      RoundRobinPool(2).withSupervisorStrategy(supervisorStrategy)
    val prop: Props = Props[Worker].withRouter(routerConfig)

    val worker = system.actorOf(prop, "Worker")
    (1 to 10).foreach { p =>
      worker ! DoIt
    }

    Thread.sleep(40000)
    system.terminate()
  }
}

我做错了什么?

1 个答案:

答案 0 :(得分:2)

你做错的一件事是在演员中加入Thread.sleep(900);删除该调用,因为它可能导致奇怪的行为。

此外,即使启用了重新启动策略,您对始终看到10个"work done"打印语句的期望也是错误的。

如你所知,你的演员的行为是不确定的:

val dt = LocalDateTime.now().getSecond
if ((dt % 2) == 0) throw new Exception("Error")
else println("work done")

如果dt恰好是您发送给演员的所有10条DoIt条消息的奇数,那么显然您会看到10条"work done"语句。如果dt是偶数,则抛出异常并重新启动routee 。如果覆盖actor上的postRestart挂钩,则可以清楚地看到这一点:

class Worker extends Actor {
  ...
  override def postRestart(t: Throwable): Unit = {
    println(s"Restarted ${self.path} ...")
  }
}

如果重新启动了routee,则不会重新处理正在处理的DoIt消息,因为documentation表示:

  

...重新启动在actor本身之外是不可见的,但是没有重新处理发生故障的消息。

总之,如果"work done"变量恰好是所有10条dt消息的奇数,那么您将看到10 DoIt个打印语句的唯一情况。如果对于那些DoIt消息中的任何一个,dt是偶数,则重新启动处理该消息的routee。伴随重新启动,特定的DoIt消息重新处理,您将看到少于10个"work done"打印语句(如果有)。