重新启动Akka Actor和反应流集成

重新启动Akka Actor和反应流集成,akka,Akka,我有一个场景,我必须从父演员中重新启动子演员。应按照以下规则重新启动: 只有在停止完成后才能启动 停止和启动都应该异步进行 我现在有以下场景: 在我的父Actor中,我有一个Monix Observable,它正在推送事件,如下所示: class ParentActor extends Actor { ... override def preStart(): Unit = { super.preStart() // Observable to stream eve

我有一个场景,我必须从父演员中重新启动子演员。应按照以下规则重新启动:

只有在停止完成后才能启动 停止和启动都应该异步进行 我现在有以下场景:

在我的父Actor中,我有一个Monix Observable,它正在推送事件,如下所示:

class ParentActor extends Actor {
  ...

    override def preStart(): Unit = {
    super.preStart()

    // Observable to stream events regarding PowerPlant's
    val powerPlantEventObservable =
    // For every config.database.refreshInterval in seconds
      Observable.interval(config.database.refreshInterval)
        // We ask the actor for the latest messages
        .map(_ => (dbServiceActor ? DBServiceActor.PowerPlantEvents).mapTo[PowerPlantEventsSeq])
        .concatMap(Observable.fromFuture(_))
        .concatMap(Observable.fromIterable(_))

    // Subscriber that pipes the messages to this Actor
    cancelable := powerPlantEventObservable.subscribe { update =>
      (self ? update).map(_ => Continue)
    }
  }
}
所以上面发生的事情是,我向另一个名为DBServiceActor的参与者询问事件列表,当这些事件可用时,我会将其传递给父参与者自己?使现代化ParentActor的receive方法如下所示,这就是我要重新启动子actor的地方。异步:

    override def receive: Receive = {

        case PowerPlantUpdateEvent(id, powerPlantCfg) =>
          log.info(s"Re-starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          // I want to stop the actor first by finding it from the actor system
          // If it exists, do a context.stop on the Actor instance
          // Once it is stopped, I want to start it again by creating a new instance of this Actor
          // Once this new Actor instance is created, I want to signal my Monix Observer to send me the next event
      }
有什么建议吗

编辑:根据以下帖子的建议,这里是新的接收方法:

def receive: Receive = {

    // TODO: When I restart, I need the powerPlantCfg!!! How to get it?
    case Terminated(actorRef) =>
      context.unwatch(actorRef)

    case PowerPlantCreateEvent(id, powerPlantCfg) =>
      log.info(s"Starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

      // Start the PowerPlant, and pipe the message to self
      startPowerPlant(id, powerPlantCfg).pipeTo(self)

    case PowerPlantUpdateEvent(id, powerPlantCfg) =>
      log.info(s"Re-starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

      context.child(s"$simulatorActorNamePrefix$id") match {
        case Some(actorRef) =>
          context.watch(actorRef)
          actorRef ! PoisonPill


        case None =>
          log.warning(s"No running actor instance found for id $id :: Creating a new instance")
          self ! PowerPlantCreateEvent(id, powerPlantCfg)
      }

    case PowerPlantDeleteEvent(id, powerPlantCfg) => // TODO
      log.info(s"Stopping PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

      context.child(s"$simulatorActorNamePrefix$id") match {
        case Some(actorRef) =>
          context.watch(actorRef)
          actorRef ! PoisonPill

        case None =>
          log.warning(s"No running actor instance found for id $id")
      }
  }

我建议如下:

// I want to stop the actor first by finding it from the actor system
要查找子级,请使用val child=context.childname或浏览context.childrename

对于上面找到的孩子,您必须设置死亡观察,以便下一步使用并杀死它:

context.watch(child) // you could also consider using context.watchWith()
child ! PoisonPill

由于上面的deatch watch设置,您的家长参与者将收到通知,孩子已停止,只需添加一个消息处理程序:

override def receive: Receive = {
  case Terminated(child) => // you might want to check which specific child was terminated if you care
    context.unwatch(child)
    context.actorOf(...) // recreate the child actor


您可以在调用context.actorOf后立即发出创建信号,如果您想确保子参与者成功启动,即,因为在初始化/启动过程中可能会出现故障,那么您应该让您的父参与者期望来自新创建的子参与者的信号。您可以在启动前将该信号从子级发送到父级,如果启动更复杂,也可以在其消息处理程序中发送。

因此,根据Frederic A的回答,我在这里发布了我的解决方案,展示了如何处理参与者的重新启动:

def waitForRestart(source: ActorRef, newMessage: Option[PowerPlantCreateEvent[PowerPlantConfig]]): Receive = {
    case Terminated(actor) =>
      context.unwatch(actor)

      newMessage match {
        case Some(powerPlantCreateEvent) =>
          self ! powerPlantCreateEvent
        case _ =>
          // just ignore
      }
      // Now unstash all of the messages
      unstashAll()

    case someDamnThing =>
      log.error(s"Unexpected message $someDamnThing :: " +
        s"received while waiting for an actor to be stopped")
      stash()
  }
以下是接收方法:

def receive: Receive = {

        case Terminated(actorRef) =>
          context.unwatch(actorRef)

        case PowerPlantCreateEvent(id, powerPlantCfg) =>
          log.info(s"Starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          // Start the PowerPlant, and pipe the message to self
          startPowerPlant(id, powerPlantCfg).pipeTo(self)

        case PowerPlantUpdateEvent(id, powerPlantCfg) =>
          log.info(s"Re-starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          context.child(s"$simulatorActorNamePrefix$id") match {
            case Some(actorRef) =>
              context.watch(actorRef)
              // We first kill
              actorRef ! PoisonPill

              // We wait asynchronously until this Actor is re-started
              context.become(
                waitForRestart(
                  actorRef,
                  Some(PowerPlantCreateEvent(id, powerPlantCfg))
                )
              )

            case None =>
              log.warning(s"No running actor instance found for id $id :: Creating a new instance")
              self ! PowerPlantCreateEvent(id, powerPlantCfg)
          }

        case PowerPlantDeleteEvent(id, powerPlantCfg) =>
          log.info(s"Stopping PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          context.child(s"$simulatorActorNamePrefix$id") match {
            case Some(actorRef) =>
              context.watch(actorRef)
              actorRef ! Kill

            case None =>
              log.warning(s"No running actor instance found for id $id")
          }
      }

我在哪里可以再次取消跟踪?我看到的问题是,当前消息在发送毒药后立即完成,而在那里,发送给Monix观察者的信号完成,这意味着观察者将把下一条消息推送到我的接收中。但是我想避免这种情况,只有在我重新启动这个新实例后,像上面Terminatedchild块中那样,我才让Monix observer推送下一条消息。我在我的原始帖子中添加了这个块,显示了我是如何将消息从我的Monix observer传送到此Actor的。对于您的第一条评论,我编辑了我的答案。对于其余部分,我建议您将消息隐藏起来,直到孩子重新启动。查看akka的文档以获取隐藏消息。隐藏可能有效!谢谢你的建议!你帮了我!
// Once this new Actor instance is created, I want to signal my Monix Observer to send me the next event
def waitForRestart(source: ActorRef, newMessage: Option[PowerPlantCreateEvent[PowerPlantConfig]]): Receive = {
    case Terminated(actor) =>
      context.unwatch(actor)

      newMessage match {
        case Some(powerPlantCreateEvent) =>
          self ! powerPlantCreateEvent
        case _ =>
          // just ignore
      }
      // Now unstash all of the messages
      unstashAll()

    case someDamnThing =>
      log.error(s"Unexpected message $someDamnThing :: " +
        s"received while waiting for an actor to be stopped")
      stash()
  }
def receive: Receive = {

        case Terminated(actorRef) =>
          context.unwatch(actorRef)

        case PowerPlantCreateEvent(id, powerPlantCfg) =>
          log.info(s"Starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          // Start the PowerPlant, and pipe the message to self
          startPowerPlant(id, powerPlantCfg).pipeTo(self)

        case PowerPlantUpdateEvent(id, powerPlantCfg) =>
          log.info(s"Re-starting PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          context.child(s"$simulatorActorNamePrefix$id") match {
            case Some(actorRef) =>
              context.watch(actorRef)
              // We first kill
              actorRef ! PoisonPill

              // We wait asynchronously until this Actor is re-started
              context.become(
                waitForRestart(
                  actorRef,
                  Some(PowerPlantCreateEvent(id, powerPlantCfg))
                )
              )

            case None =>
              log.warning(s"No running actor instance found for id $id :: Creating a new instance")
              self ! PowerPlantCreateEvent(id, powerPlantCfg)
          }

        case PowerPlantDeleteEvent(id, powerPlantCfg) =>
          log.info(s"Stopping PowerPlant actor with id = $id and type ${powerPlantCfg.powerPlantType}")

          context.child(s"$simulatorActorNamePrefix$id") match {
            case Some(actorRef) =>
              context.watch(actorRef)
              actorRef ! Kill

            case None =>
              log.warning(s"No running actor instance found for id $id")
          }
      }