Akka 推荐实践来初始化 `PersistentActor`

Akka recommend pratice to init `PersistentActor`

case class State(id: Long, remain: Int) {
  def take(t: Take) = copy(remain = remain - t.amount) 
}

object StateService {
  def getInitState(id: Long): Future[State]
}

sealed trait Evt
case class Init(id: Long) extends Evt
case class Take(id: Long, amount: Int) extends Evt

class FooActor extends PersistentActor  {
  var state: State

  def receiveCommand = {
    case Init(id) => ??? // how to
    case t: Take => persistAsync(t) {case e => state = state.take(t)}
  }
}

object FooActor {

}

如示例所述

如何在接受任何其他命令之前初始化 actor 状态?

您可以使用不同的行为:

case class State(id: Long, remain: Int)

object StateService {
  def getInitState(id: Long): Future[State]
}

sealed trait Evt
case class Init(id: Long) extends Evt

class FooActor extends PersistentActor  {
  var state: State

  import akka.pattern.pipe

  def notInitialized: Receive = {
    case Init(id) => 
      // for simplicity, failure is not handled
      StateService.getInitState(id) pipeTo self
    case st: State =>
      state = st
      context become initialized
  }

  def initialized: Receive = {
    case _ => // meh
  }

  def receiveCommand = notInitialized
}

object FooActor {

}

您甚至可以通过将可变状态作为参数传递给 initialized 行为(例如 initialized(state))来完全删除可变状态。 关于恢复,来自官方 Akka 文档:

It's also possible to switch between different command handlers during normal processing and recovery with context.become() and context.unbecome(). To get the actor into the same state after recovery you need to take special care to perform the same state transitions with become and unbecome in the receiveRecover method as you would have done in the command handler. Note that when using become from receiveRecover it will still only use the receiveRecover behavior when replaying the events. When replay is completed it will use the new behavior.