How to deal with long initialization of an Akka child actor?

元气小坏坏 提交于 2020-01-30 19:32:44

问题


I have an actor which creates a child actor to perform some lengthy computations.

The problem is that the initialization of the child actor takes a few seconds and all messages that the parent actor sends to the child between it is created and gets fully initialized are dropped.

This is the logic of the code that I am using:

class ChildActor extends Actor {
  val tagger = IntializeTagger(...) // this takes a few seconds to complete

  def receive = {
    case Tag(text) => sender ! tagger.tag(text)
    case "hello" => println("Hello")
    case _ => println("Unknown message")
  }
}

class ParentActor extends Actor {
  val child = context.ActorOf(Props[ChildActor], name = "childactor")

  // the below two messages seem to get lost
  child ! "hello"
  child ! Tag("This is my sample text")

  def receive = {
     ...
  }
}

How could I get around that problem? Is it possible to make the parent actor wait until the child is fully initialized? I will be using the child actor with routing and possibly on remote actor systems.

EDIT

Following drexin's advice I have change my code into:

class ChildActor extends Actor {
  var tagger: Tagger = _

  override def preStart() = {
    tagger = IntializeTagger(...) // this takes a few seconds to complete
  }

  def receive = {
    case Tag(text) => sender ! tagger.tag(text)
    case "hello" => println("Hello")
    case _ => println("Unknown message")
  }
}

class ParentActor extends Actor {
  var child: ActorRef = _

  override def preStart() = {
    child = context.ActorOf(Props[ChildActor], name = "childactor")

    // When I add
    // Thread.sleep(5000)
    // here messages are processed without problems

    // wihout hardcoding the 5 seconds waiting 
    // the below two messages seem to get lost
    child ! "hello"
    child ! Tag("This is my sample text")
  }

  def receive = {
     ...
  }
}

but the problem remains. What am I missing?


回答1:


Don't initialize the tagger in the constructor, but in the preStart hook, this way the messages will be collected in the message box and delivered, when the actor is ready.

edit:

You should do the same for the actor creation in your ParentActor class, because you would have the same problem, if the ChildActor would respond, before the ParentActor is initialized.

edit2:

I created a simple example, but I was not able to reproduce your problems. Following code works perfectly fine:

import akka.actor._

case class Tag(x: String)

class ChildActor extends Actor {
  type Tagger = String => String
  var tagger: Tagger = _

  override def preStart() = {
    tagger = (x: String) => x+"@tagged" // this takes a few seconds to complete
    Thread.sleep(2000) // simulate time taken to initialize Tagger
  }

  def receive = {
    case Tag(text) => sender ! tagger(text)
    case "hello" => println("Hello")
    case _ => println("Unknown message")
  }
}

class ParentActor extends Actor {
  var child: ActorRef = _

  override def preStart() = {
    child = context.actorOf(Props[ChildActor], name = "childactor")

    // When I add
    // Thread.sleep(5000)
    // here messages are processed without problems

    // wihout hardcoding the 5 seconds waiting 
    // the below two messages seem to get lost
    child ! "hello"
    child ! Tag("This is my sample text")
  }

  def receive = {
    case x => println(x)
  }
}

object Main extends App {

  val system = ActorSystem("MySystem")

  system.actorOf(Props[ParentActor])
}

Output is:

[info] Running Main
Hello
This is my sample text@tagged



回答2:


I think what you might be looking for is the combo of Stash and become. The idea will be that the child actor will set it's initial state to uninitialized, and while in this state, it will stash all incoming messages until it is fully initialized. When it's fully initialized, you can unstash all of the messages before swapping behavior over to the initialized state. A simple example is as follows:

class ChildActor2 extends Actor with Stash{
  import context._
  var dep:SlowDependency = _

  override def preStart = {
    val me = context.self
    Future{
      dep = new SlowDependency
      me ! "done"
    }
  }

  def uninitialized:Receive = {
    case "done" => 
      unstashAll
      become(initialized) 
    case other => stash()
  }

  def initialized:Receive = {
    case "a" => println("received the 'a' message")
    case "b" => println("received the 'b' message")   
  }

  def receive = uninitialized
}

Notice in the preStart that I'm doing my initialization asynchronously, so as to not halt the startup of the actor. Now this is a bit ugly, with closing over the mutable dep var. You could certainly handle it by instead sending a message to another actor that handles instantiation of the slow dependency and sends it back to this actor. Upon receiving the dependency, it will then call become for the initialized state.

Now there is one caveat with Stash and I'll paste it in right from the Akka docs:

Please note that the Stash can only be used together with actors that 
have a deque-based mailbox. For this, configure the mailbox-type of the 
dispatcher to be a deque-based mailbox, such as 
akka.dispatch.UnboundedDequeBasedMailbox (see Dispatchers (Scala)). 

Now if this does not suite you, you can try a more DI type approach and let the slow dependency be injected into the child actor via it's constructor. So you would define the child actor like so:

class ChildActor(dep:SlowDependency) extends Actor{
    ...
} 

Then when starting up this actor, you would do it as follows:

context.actorOf(new Props().withCreator(new ChildActor(slowDep)), name = "child-actor")



回答3:


I would suggest to send a "ready" message from the child actor to the parent and start sending messages to the child actor only after this message will be received. You can do it just in receive() method for simple use cases or you can use become or FSM to change parent actor behavior after the child will be initialized (for example, store the messages for the child in some intermediate storage and send them all when it will be ready).



来源:https://stackoverflow.com/questions/17061740/how-to-deal-with-long-initialization-of-an-akka-child-actor

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!