по запросу актор получить или создать

Я могу создавать актеров с помощью actorOf и смотреть их с помощью actorFor. Теперь я хочу получить актера с помощью некоторого id:String, и если он не существует, я хочу, чтобы он был создан. Что-то вроде этого:

  def getRCActor(id: String):ActorRef = {
    Logger.info("getting actor %s".format(id))
    var a = system.actorFor(id)
    if(a.isTerminated){
      Logger.info("actor is terminated, creating new one")
      return system.actorOf(Props[RC], id:String)
    }else{
      return a
    }
   }

Но это не работает, так как isTerminated всегда верно, и я получаю исключение actor name 1 is not unique! для второго вызова. Я думаю, что я использую неправильный шаблон здесь. Может кто-нибудь помочь, как этого добиться? я нуждаюсь

  • Создание актеров по запросу
  • Поиск актеров по идентификатору и, если их нет, создать их
  • Возможность уничтожить дальше, так как я не знаю, понадобится ли мне это снова

Должен ли я использовать Dispatcher или Router для этого?

Решение Как было предложено, я использую конкретный супервизор, который содержит доступных актеров на карте. Можно попросить предоставить одного из его детей.

class RCSupervisor extends Actor {

  implicit val timeout = Timeout(1 second)
  var as = Map.empty[String, ActorRef]

  def getRCActor(id: String) = as get id getOrElse {
    val c = context actorOf Props[RC]
    as += id -> c
    context watch c
    Logger.info("created actor")
    c
  }

  def receive = {

    case Find(id) => {
      sender ! getRCActor(id)
    }

    case Terminated(ref) => {
      Logger.info("actor terminated")
      as = as filterNot { case (_, v) => v == ref }
    }
  }
}

Его компаньон объект

object RCSupervisor {

  // this is specific to Playframework (Play's default actor system)
  var supervisor = Akka.system.actorOf(Props[RCSupervisor])

  implicit val timeout = Timeout(1 second)

  def findA(id: String): ActorRef = {
    val f = (supervisor ? Find(id))
    Await.result(f, timeout.duration).asInstanceOf[ActorRef]
  }
  ...
}

person martin    schedule 26.05.2012    source источник


Ответы (4)


Я не так давно пользуюсь akka, но создатель акторов по умолчанию является их руководителем. Следовательно, родитель может прослушивать их завершение;

var as = Map.empty[String, ActorRef] 
def getRCActor(id: String) = as get id getOrElse {
  val c = context actorOf Props[RC]
  as += id -> c
  context watch c
  c
}

Но, очевидно, вам нужно следить за их Прекращением;

def receive = {
  case Terminated(ref) => as = as filterNot { case (_, v) => v == ref }

Это решение? Должен сказать, я не совсем понял, что вы имели в виду под "terminated is always true => имя актера 1 не уникально!"

person oxbow_lakes    schedule 26.05.2012
comment
Да это решение. Я пытался не создавать свой собственный реестр (карту) и использовать для этого путь к актеру. Но я все равно планировал внедрить супервизор, и похоже, что другого пути нет. Некоторая предыстория: я использую Playframework и предоставленный им контекст. - person martin; 27.05.2012
comment
Спасибо за этот ответ. Мне никогда не приходило в голову, что родительский актор увидит сообщение Terminated. Это имеет полный смысл, но я был зациклен на том, чтобы следить за ребенком, чтобы справиться с завершенным (что имеет гораздо меньше смысла)... - person jxstanford; 27.05.2012
comment
Похоже, нормальное решение, но я не слишком люблю использовать этот var где-либо рядом с параллельными вещами. mutable.Map немного лучший вариант? - person Ashesh; 05.08.2016

Актеры могут быть созданы только их родителем, и из вашего описания я предполагаю, что вы пытаетесь заставить систему создать актера не верхнего уровня, что всегда будет терпеть неудачу. Что вам нужно сделать, так это отправить сообщение родителю со словами «дайте мне этого ребенка здесь», тогда родитель может проверить, существует ли он в настоящее время, находится ли он в добром здравии и т. д., возможно, создать новый, а затем ответить соответствующим сообщением. сообщение о результате.

Повторим этот чрезвычайно важный момент: получение или создание может быть выполнено ТОЛЬКО прямым родителем.

person Roland Kuhn    schedule 27.05.2012
comment
Ты прав. Я выбрал ответ oxbow_lakes, поскольку он предоставил пример кода. Смотрите мой комментарий там. Спасибо - person martin; 27.05.2012

Я основывал свое решение этой проблемы на коде/предложении oxbow_lakes, но вместо создания простой коллекции всех дочерних акторов я использовал (двунаправленную) карту, которая может быть полезна, если количество дочерних акторов значительно.

import play.api._
import akka.actor._
import scala.collection.mutable.Map 

trait ResponsibleActor[K] extends Actor {
  val keyActorRefMap: Map[K, ActorRef] = Map[K, ActorRef]()
  val actorRefKeyMap: Map[ActorRef, K] = Map[ActorRef, K]()

  def getOrCreateActor(key: K, props: => Props, name: => String): ActorRef = {
    keyActorRefMap get key match {
      case Some(ar) => ar
      case None =>  {
        val newRef: ActorRef = context.actorOf(props, name)
        //newRef shouldn't be present in the map already (if the key is different)
        actorRefKeyMap get newRef match{
          case Some(x) => throw new Exception{}
          case None =>
        }
        keyActorRefMap += Tuple2(key, newRef)
        actorRefKeyMap += Tuple2(newRef, key)
        newRef
      }
    }
  }

  def getOrCreateActorSimple(key: K, props: => Props): ActorRef = getOrCreateActor(key, props, key.toString)

  /**
   * method analogous to Actor's receive. Any subclasses should implement this method to handle all messages
   * except for the Terminate(ref) message passed from children
   */
  def responsibleReceive: Receive

  def receive: Receive = {
    case Terminated(ref) => {
      //removing both key and actor ref from both maps
      val pr: Option[Tuple2[K, ActorRef]] = for{
        key <- actorRefKeyMap.get(ref)
        reref <- keyActorRefMap.get(key)
      } yield (key, reref)

      pr match {
        case None => //error
        case Some((key, reref)) => {
          actorRefKeyMap -= ref
          keyActorRefMap -= key
        }
      }
    }
    case sth => responsibleReceive(sth)
  }
}

Чтобы использовать эту функциональность, вы наследуете от ResponsibleActor и реализуете responsibleReceive. Примечание. Этот код еще не был полностью протестирован и может иметь некоторые проблемы. Я опустил некоторую обработку ошибок, чтобы улучшить читабельность.

person nietaki    schedule 09.12.2012

В настоящее время вы можете использовать внедрение зависимостей Guice с Akka, что объясняется на странице http://www.lightbend.com/activator/template/activator-akka-scala-guice. Вы должны создать сопутствующий модуль для актера. Затем в его методе configure вам нужно создать именованную привязку к классу актера и некоторым свойствам. Свойства могут исходить из конфигурации, в которой, например, для субъекта настроен маршрутизатор. Вы также можете поместить туда конфигурацию маршрутизатора программно. Везде, где вам нужна ссылка на актера, вы вводите его с помощью @Named("actorname"). Настроенный маршрутизатор создаст экземпляр актора, когда это необходимо.

person Werner Donné    schedule 15.03.2017