在能够处理其他一些消息之前初始化一个actor

时间:2013-12-15 04:13:09

标签: scala asynchronous akka future

我有一个创造另一个的演员:

class MyActor1 extends Actor {
  val a2 = system actorOf Props(new MyActor(123))
}

第二个actor必须在创建后初始化(bootstrap),并且只有在此之后它必须能够完成其他工作。

class MyActor2(a: Int) extends Actor {
  //initialized (bootstrapped) itself, potentially a long operation 
  //how?
  val initValue = // get from a server

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

所以MyActor2必须做的第一件事就是做一些初始化自己的工作。这可能需要一些时间,因为它是对服务器的请求。只有在成功完成后,它才能通过receive处理传入的消息。在那之前 - 它一定不能这样做。

当然,对服务器的请求必须是异步的(最好是使用Future,而不是asyncawait或其他高级别的内容,例如AsyncHttpClient。我知道如何使用Future,但这不是问题。

我如何确保?

p.s。我的猜测是它必须首先向自己发送消息。

2 个答案:

答案 0 :(得分:10)

您可以使用become方法在初始化后更改actor的行为:

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
  }
}

请注意,此类actor将在初始化之前删除所有消息。您必须手动保留这些消息,例如使用Stash

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

  ...

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

如果您不想使用var进行初始化,可以使用InitializationData创建初始化行为,如下所示:

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)
  }
}

答案 1 :(得分:5)

我不知道提议的解决方案是个好主意。 发送初始化消息对我来说似乎很尴尬。演员有一个生命周期并提供一些钩子。当您查看API时,您将发现prestart挂钩。

因此我建议如下:

  • 创建actor时,会运行preStart挂钩,执行返回未来的服务器请求。
  • 虽然未来未完成,但所有收到的消息都被隐藏起来。
  • 当未来完成时,它使用context.become来使用您的真实/普通接收方法。
  • 成为你解开一切之后。

以下是代码的草图(不良解决方案,请参阅下面的实际解决方案):

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
  }
}

更新:根据Senias反馈改进解决方案

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
}