Подтвердить что ты не робот

По требованию актер получает или создает

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

  • Создание участников по требованию
  • Поиск действующих лиц по id, а если нет, создайте их
  • Способность уничтожать, так как я не знаю, понадобится ли она снова.

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

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

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

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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