我有一个简单的“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()
}
}
我做错了什么?
答案 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"
打印语句(如果有)。