当使用context.actorOf由另一个actor创建重新启动的actor时发生了什么?

时间:2015-11-29 07:23:41

标签: scala akka

package actor.faulttolerance

import akka.actor.SupervisorStrategy.Restart
import akka.actor._
import akka.event.LoggingReceive

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
/**
  * Created by lc on 2015/11/28.
  */
object CounterService {

  case class Increment(n: Int)

  case object GetCurrentCount

  case class CurrentCount(key: String, value: Long)

  case class ServiceUnavailable(msg: String) extends RuntimeException

  private case object Reconnect

}

class CounterService extends Actor {

  import Counter._
  import CounterService._
  import Storage._

  override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 5 seconds) {
    case _: StorageException => Restart
  }
  val key = self.path.name
  var storage: Option[ActorRef] = None
  var counter: Option[ActorRef] = None
  var backlog = IndexedSeq.empty[(ActorRef, Any)]
  val MaxBacklog = 10000


  override def preStart(): Unit = {
    initStorage()
  }

  /**
    * *
    * The child storage is restarted in case of failure, but after 3 restarts,
    * and still failing it will be stopped. Better to back-off than continuously
    * failing. When it has been stopped we will schedule a Reconnect after a delay
    * Watch the child so we receive Terminated message when it has been terminated
    */
  def initStorage(): Unit = {
    storage = Some(context.watch(context.actorOf(Props[Storage], name = "storage")))
    counter foreach {
      _ ! UseStorage(storage)
    }
    storage.get ! Get(key)
  }

  def receive = LoggingReceive {
    case Entry(k, v) if k == key && counter == None =>
      // Reply from Storage of the initial value, now we can create the Counter
      val c = context.actorOf(Props(classOf[Counter], key, v))
      counter = Some(c)
      // Tell the counter to use current storage
      c ! UseStorage(storage)
      // and send the buffered backlog to the counter
      for ((replyTo, msg) <- backlog) c.tell(msg, sender = replyTo)
      backlog = IndexedSeq.empty
    case msg@Increment(n) => forwardOrPlaceInBacklog(msg)
    case msg@GetCurrentCount => forwardOrPlaceInBacklog(msg)
    case Terminated(actorRef) if Some(actorRef) == storage =>
      // After 3 restarts the storage child is stopped.
      // We receive Terminated because we watch the child, see initStorage.
      storage = None
      // Tell the counter that there is no storage for the moment
      counter foreach {
        _ ! UseStorage(None)
      }
      // Try to re-establish storage after while
      context.system.scheduler.scheduleOnce(10 seconds, self, Reconnect)
    case Reconnect =>
      // Re-establish storage after the scheduled delay
      initStorage()
  }


  def forwardOrPlaceInBacklog(msg: Any) {

    // We need the initial value from storage before we can start delegate to
    // the counter. Before that we place the messages in a backlog, to be sent
    // to the counter when it is initialized.
    counter match {
      case Some(c) => c forward msg
      case None =>
        if (backlog.size >= MaxBacklog)
          throw new ServiceUnavailable(
            "CounterService not available, lack of initial value")
        backlog :+= (sender() -> msg)
    }
  }

}

代码来自akka doc,如果子actor存储失败3次,主管actor CounterService将发出Restart。在存储actor重启期间,当stoped时,CounterService actor将监视存储actor的死亡,获取Terminated消息,将其存储引用设置为None,并在10秒后重新连接。在重新连接时,CounterService actor将使用:storage = Some(context.watch(context.actorOf(Props[Storage], name = "storage")))创建一个新的存储角色。

从akka doc我知道:

  1. 重新启动Actor会创建一个新的actor实例,只需交换它。
  2. context.actorOf将创建一个新的actor ref。
  3. 因此,通过重新启动并由CounterService actor的context.actorOf再次创建存储actor吗?通过重新启动创建的演员会被遗忘吗? 或者当contex.actorOf看到存储角色已经重新启动并进行实例交换时,它只是使用新的并且并不总是创建一个新的?我看了医生,但没找到线索。

1 个答案:

答案 0 :(得分:0)

通过仔细阅读akka doc找到答案。

重启频率设置了限制。在上面的代码中,它在5秒内最多重试3次。如果超出限制,则停止子actor。 因此前3次存储演员失败,演员立即重启。它是主管,CounterService什么都不做。重试3次或5秒后,存储actor被停止,CounterService在10秒内收到存储actor终止消息和rennect。

所以: 1.当contex.actorOf看到存储actor已经重新启动并进行实例交换时,它只是使用新的并不总是创建一个新的? 当存储actor重启时,CounterService actor将不知道。 2.重新启动创建的演员会被遗忘吗?  它将在重试限制后停止。