Initializing an actor before being able to handle some other messages

泄露秘密 提交于 2019-12-01 04:10:59

You could use become method to change actor's behavior after initialization:

class MyActor2(a: Int) extends Actor {

  server ! GetInitializationData

  def initialize(d: InitializationData) = ???

  //handle incoming messages
  val initialized: Receive = {
    case "job1" => // do some job but after it's initialized (bootstrapped) itself
  }

  def receive = {
    case d @ InitializationData =>
      initialize(d)
      context become initialized
  }
}

Note that such actor will drop all messages before initialization. You'll have to preserve these messages manually, for instance using Stash:

class MyActor2(a: Int) extends Actor with Stash {

  ...

  def receive = {
    case d @ InitializationData =>
      initialize(d)
      unstashAll()
      context become initialized
    case _ => stash()
  }
}

If you don't want to use var for initialization you could create initialized behavior using InitializationData like this:

class MyActor2(a: Int) extends Actor {

  server ! GetInitializationData

  //handle incoming messages
  def initialized(intValue: Int, strValue: String): Receive = {
    case "job1" => // use `intValue` and `strValue` here
  }

  def receive = {
    case InitializationData(intValue, strValue) =>
      context become initialized(intValue, strValue)
  }
}

I don't know wether the proposed solution is a good idea. It seems awkward to me to send a Initialization message. Actors have a lifecycle and offer some hooks. When you have a look at the API, you will discover the prestart hook.

Therefore i propose the following:

  • When the actor is created, its preStart hook is run, where you do your server request which returns a future.
  • While the future is not completed all incoming messages are stashed.
  • When the future completes it uses context.become to use your real/normal receive method.
  • After the become you unstash everything.

Here is a rough sketch of the code (bad solution, see real solution below):

class MyActor2(a: Int) extends Actor with Stash{

  def preStart = {
    val future = // do your necessary server request (should return a future)
    future onSuccess {
      context.become(normalReceive)
      unstash()
    }
  }

  def receive = initialReceive

  def initialReceive = {
    case _ => stash()
  }

  def normalReceive = {
    // your normal Receive Logic
  }
}

UPDATE: Improved solution according to Senias feedback

class MyActor2(a: Int) extends Actor with Stash{

  def preStart = {
    val future = // do your necessary server request (should return a future)
    future onSuccess {
      self ! InitializationDone
    }
  }

  def receive = initialReceive

  def initialReceive = {
    case InitializationDone =>
      context.become(normalReceive)
      unstash()
    case _ => stash()
  }

  def normalReceive = {
    // your normal Receive Logic
  }

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