У меня есть следующая настройка актеров с использованием актеров Akka (2.10)

A -спаун-> B -спаун-> C

A -sendWork-> B -sendWork-> C

C -sendResults-> A (повторно)

Однако в какой-то момент A замечает, что он должен изменить рабочую нагрузку, отправляемую на B / C, потому что C отправляет большое количество сообщений, которые оказываются бесполезными. Однако в таких ситуациях почтовый ящик C кажется очень переполненным и / или C может быть заблокирован.

Как A может сказать B немедленно выключить C? Потеря состояния и сообщений как B, так и C приемлема, поэтому их можно уничтожить и создать новые.

0
mitchus 27 Сен 2013 в 11:17

1 ответ

Лучший ответ

Если акторы запускаются так, как вы описали, то правильное использование stop сделает то, что вам нужно. Согласно документам, вызов stop будет:

1) запретить попадание дополнительных сообщений в почтовый ящик (отправлено на мертвую букву)

2) возьмите текущее содержимое почтового ящика и также отправьте его в мертвую рассылку (хотя это основано на имплантации почтового ящика, но дело в том, что они не будут обрабатываться)

Теперь, если актеру нужно будет полностью закончить сообщение, которое он в настоящее время обрабатывает, прежде чем оно будет полностью остановлено, поэтому, если оно "зависло", остановка (или что-то еще в этом отношении) не исправит это, но я не думаю, что это ситуация, которую вы описываете.

Я собрал небольшой образец кода для демонстрации. По сути, A отправляет сообщение B, чтобы начать отправку работы C. B наводняет C некоторой работой, а C отправляет результаты этой работы обратно A. Когда определенное количество ответов будет получено A, он будет инициировать остановку B и C путем остановки B. Когда B полностью остановлен, он затем перезапустит процесс снова, до 2 раз, потому что он останавливается сам. Код выглядит так:

case object StartWork
case class DoWork(i:Int, a:ActorRef)
case class WorkResults(i:Int)

class ActorA extends Actor{
  import context._
  var responseCount = 0
  var restarts = 0

  def receive = startingWork

  def startingWork:Receive = {
    case sw @ StartWork =>
      val myb = actorOf(Props[ActorB])
      myb ! sw      
      become(waitingForResponses(myb))
  }

  def waitingForResponses(myb:ActorRef):Receive = {
    case WorkResults(i) =>
      println(s"Got back work results: $i")
      responseCount += 1
      if (responseCount > 200){
        println("Got too many responses, terminating children and starting again")
        watch(myb)
        stop(myb)
        become(waitingForDeath)
      }
  }

  def waitingForDeath:Receive = {
    case Terminated(ref) => 
      restarts += 1
      if (restarts <= 2){
        println("children terminated, starting work again") 
        responseCount = 0
        become(startingWork)
        self ! StartWork
      }
      else{
        println("too many restarts, stopping self")
        context.stop(self)
      }

  }
}

class ActorB extends Actor{
  import concurrent.duration._
  import context._  
  var sched:Option[Cancellable] = None

  override def postStop = {
    println("stopping b")
    sched foreach (_.cancel)
  }

  def receive = starting

  def starting:Receive = {
    case sw @ StartWork =>
      val myc = context.actorOf(Props[ActorC])
      sched = Some(context.system.scheduler.schedule(1 second, 1 second, self, "tick"))
      become(sendingWork(myc, sender))
  }

  def sendingWork(myc:ActorRef, a:ActorRef):Receive = {
    case "tick" => 
      for(j <- 1 until 1000) myc ! DoWork(j, a)

  }
}

class ActorC extends Actor{
  override def postStop = {
    println("stopping c")
  }
  def receive = {
    case DoWork(i, a) =>
      a ! WorkResults(i)      
  }
}

Это немного грубовато по краям, но должно показать, что каскадирование остановки от B до C не позволит C отправлять ответы обратно A, даже если у него все еще есть сообщения в почтовом ящике. Надеюсь, это то, что вы искали.

1
cmbaxter 27 Сен 2013 в 15:59