当后台演员必须完成任务时延迟关闭

时间:2018-02-13 09:54:12

标签: scala akka

我有一个Akka应用程序,其中一个路由器组由执行某些作业的actor组成。当我检测到我的应用程序关闭时,我希望我的演员在完全关闭应用程序之前完成他们的工作。我的问题的用例是在重新部署的情况下:如果当前的工作没有被执行,我不想授权它。

要检测我的应用程序的关闭,我使用以下代码:

scala.sys.addShutdownHook { 
// let actors finished their work
}

为了进行一些测试,我添加一个无限循环来查看关闭挂钩是否被阻止但是应用程序结束,所以这不是我预期的行为。

为了让我的演员完成他们的工作,我将在以下文章中实现这个想法:http://letitcrash.com/post/30165507578/shutdown-patterns-in-akka-2

所以现在我正在寻找一种方法来忽略关闭钩子,并在我的工作人员执行完所有工作后关闭所有资源和应用程序。

@ Edmondo1984评论后更新

我的主要应用:

val workers = this.createWorkerActors()
val masterOfWorkers = system.actorOf(Master.props(workers), name = "master")

    this.monitorActors(supervisor,workers,masterOfWorkers)
    this.addShutDownHook(system,masterOfWorkers,supervisor)

def monitorActors(supervisor : ActorRef,workers : List[ActorRef], master : ActorRef) : Unit = {
    val actorsToMonitor = master +: workers
    supervisor ! MonitorActors(actorsToMonitor)
  }

  def addShutDownHook
  (
    system : ActorSystem,
    masterOfWorkers : ActorRef, // actor wrapping a ClusterGroup router, brodcasting a PoisonPill to each worker
    supervisor : ActorRef
  ) : Unit = {
    scala.sys.addShutdownHook {
      implicit val timeout = Timeout(10.hours) // How to block here until actors are terminated ?
      system.log.info("Send a Init Shutdown to {}", masterOfWorkers.path.toStringWithoutAddress)
      masterOfWorkers ! InitShutDown
      system.log.info("Gracefully shutdown all actors of ActorSystem {}", system.name)
      Await.result((supervisor ? InitShutDown), Duration.Inf)
      system.log.info("Gracefully shutdown actor system")
      Await.result(system.terminate(), 1.minutes)
      system.log.info("Gracefully shutdown Akka management ...")
      Await.result(AkkaManagement(system).stop(), 1.minutes)
      System.exit(0)
    }
  }

主管演员

case class Supervisor()  extends Actor with ActorLogging {

  var numberOfActorsToWatch = 0L

  override def receive: Receive = {
    case MonitorActors(actorsToMonitor) =>
      log.info("Monitor {} actors, received by {}", actorsToMonitor.length, sender().path)
      this.numberOfActorsToWatch = actorsToMonitor.length
      actorsToMonitor foreach(context.watch(_))

    case Terminated(terminatedActor) if this.numberOfActorsToWatch > 0 =>
      log.info("Following actor {} is terminated. Remaining alives actors is {}", terminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
      this.numberOfActorsToWatch -= 1

    case Terminated(lastTerminatedActor) if this.numberOfActorsToWatch == 0 =>
      log.info("Following actor {} is terminated. All actors has been terminated",lastTerminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
      // what I can do here ?
      //context.stop(self)

  }
}

application.conf

akka {
actor {
coordinated-shutdown {
    default-phase-timeout = 20 s
    terminate-actor-system = off
    exit-jvm = off
    run-by-jvm-shutdown-hook = off
  }
}
}

我不知道如何阻止主线程,最终杀死了应用程序。

1 个答案:

答案 0 :(得分:1)

这可以通过在层次结构前面放置一个主管演员来轻松实现:

  • 当您需要关机时,您会向主管发送消息并缓存发件人A
  • 主管通过DeadWatch订阅儿童死亡(见https://doc.akka.io/docs/akka/2.5/actors.html
  • 主管会将计数器变量设置为子项数,然后向所有孩子发送一条消息,告诉他们尽快关闭。当孩子完成后,他们将自行终止。主管将收到通知并减少计数器
  • 当计数器达到0时,主管会向A发送一条消息说ShutdownTerminated并终止。

您的代码将如此:

class Supervisor  extends Actor with ActorLogging {

  var shutdownInitiator:ActorRef = _
  var numberOfActorsToWatch = 0L

  override def receive: Receive = {
    case InitShutdown =>
      this.numberOfActorsToWatch = context.children.length
      context.children.foreach(context.watch(_))
      context.children.foreach { s => s ! TerminateSomehow } 
      shutdownInitiator = sender
    case Terminated(terminatedActor) if this.numberOfActorsToWatch > 0 =>
      log.info("Following actor {} is terminated. Remaining alives actors is {}", terminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
      this.numberOfActorsToWatch -= 1

    case Terminated(lastTerminatedActor) if this.numberOfActorsToWatch == 0 =>
      log.info("Following actor {} is terminated. All actors has been terminated",lastTerminatedActor.path.toStringWithoutAddress, this.numberOfActorsToWatch)
      // what I can do here ?
      shutdownInitiator ! Done
      context.stop(self)

  }
}

在你的关闭钩子上,你需要一个对主管的引用并使用ask模式:

Await.result(supervisor ? InitShutdown, Duration.Inf)
actorSystem.terminate()