2012-05-26 3 views
22

Я могу создать актеров с 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! исключение для второго вызова. Наверное, я использую неправильный шаблон здесь. Может кто-то помочь, как достичь этого? Мне нужно

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

Должен ли я использовать Диспетчер или Маршрутизатор для этого?

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

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

ответ

13

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

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 не уникально!»

+0

Да, это решение. Я попытался не создавать собственный реестр (Карта) и использовать для этого Actor Path. Но в любом случае я планировал реализовать супервайзера, и, похоже, другого пути нет. Некоторые предпосылки: я использую Playframework и их предоставленный контекст – martin

+0

Спасибо за этот ответ. Мне никогда не приходило в голову, что родительский актер увидит сообщение Terminated. Это имеет смысл, но меня повесили на том, что нужно «смотреть» на ребенка, чтобы справиться с завершенным (что делает намного меньше смысла) ... – jxstanford

+0

Похоже на решение OK, но я не слишком люблю используя этот 'var' в любом месте рядом с параллельными вещами. - это 'mutable.Map' немного лучший вариант? – Ashesh

12

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

Чтобы повторить этот чрезвычайно важный момент: get-or-create может ТОЛЬКО когда-либо выполняться прямым родителем.

+0

Вы правы. Я выбрал ответ oxbow_lakes, поскольку он представил пример кода. Смотрите мой комментарий там. Спасибо – martin

1

Я основал свое решение этой проблемы по коду/предложению 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. Примечание. Этот код еще не полностью протестирован и может по-прежнему иметь некоторые проблемы. Я искал некоторые ошибки обработки, чтобы улучшить читаемость.

0

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

Смежные вопросы