2012-05-26 14 views
22

Puedo crear actores con actorOf y mirarlos con actorFor. Ahora quiero obtener un actor por unos id:String y si no existe, quiero que se cree. Algo como esto:a petición actor obtener o bien crear

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

Pero esto no funciona como isTerminated es siempre verdad y me da actor name 1 is not unique! excepción para la segunda llamada. Supongo que estoy usando el patrón incorrecto aquí. ¿Alguien puede ayudarme a lograr esto? Necesito

  • Crear actores en la demanda
  • actores en Buscar por ID y si no presentarlos crear
  • capacidad de destruir el, ya que no sé si voy a volver a necesitar

¿Debo usar un Dispatcher o enrutador para esto?

Solución Como propuse, utilizo un Supervisor concreto que mantiene a los actores disponibles en un mapa. Se le puede pedir que proporcione uno de sus hijos.

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

Su objeto acompañante

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

Respuesta

13

no he estado usando akka durante tanto tiempo, pero el creador de los actores es por defecto de su supervisor de . Por lo tanto, el padre puede escuchar su terminación;

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 
} 

Pero, obviamente, debe observar su Terminación;

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

¿Eso es una solución? Debo decir que no entendí completamente lo que quería decir con "terminado es siempre verdadero => ¡el nombre del actor 1 no es único!"

+0

Sí, esta es una solución. Traté de evitar crear mi propio Registro (Mapa) y usar Actor Path para esto. Pero planeé implementar un supervisor de todos modos y parece que no hay otra manera. Algunos antecedentes: Estoy usando Playframework y su contexto proporcionado – martin

+0

Gracias por esta respuesta. Nunca se me ocurrió que el actor principal vería el mensaje Terminado. Tiene todo el sentido, pero me quedé colgado al tener que "vigilar" al niño para manejar el terminado (lo cual tiene mucho menos sentido) ... – jxstanford

+0

Parece una solución correcta, pero no me gusta demasiado usando esa 'var' en cualquier lugar cerca de cosas concurrentes. es 'mutable.Map' una opción marginalmente mejor? – Ashesh

12

Los actores solo pueden ser creados por sus padres, y de su descripción supongo que está intentando que el sistema cree un actor no nuevo, que siempre fallará. Lo que debes hacer es enviar un mensaje al padre diciendo "dame ese niño aquí", luego el padre puede verificar si eso existe actualmente, si está en buen estado de salud, etc., posiblemente crear uno nuevo y luego responder con un mensaje apropiado. mensaje de resultado

Para reiterar este punto extremadamente importante: get-or-create SOLO puede ser hecho por el padre directo.

+0

Tienes razón. Elegí la respuesta de oxbow_lakes cuando me proporcionó alguna muestra de código. Vea mi comentario allí. Gracias – martin

1

He basado mi solución a este problema en el código/sugerencia de oxbow_lakes, pero en lugar de crear una colección simple de todos los actores secundarios utilicé un mapa (bidireccional), que podría ser beneficioso si el número de actores secundarios es significativo .

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) 
    } 
} 

para utilizar esta funcionalidad se hereda de ResponsibleActor e implementar responsibleReceive. Nota: este código aún no se ha probado exhaustivamente y podría tener algunos problemas. Omití el manejo de errores para mejorar la legibilidad.

0

Actualmente puede usar la inyección de dependencia Guice con Akka, que se explica en http://www.lightbend.com/activator/template/activator-akka-scala-guice. Tienes que crear un módulo de acompañamiento para el actor. En su método de configuración, debe crear un enlace con nombre para la clase actor y algunas propiedades. Las propiedades podrían provenir de una configuración donde, por ejemplo, un enrutador está configurado para el actor. También puede poner la configuración del enrutador allí programáticamente. En cualquier lugar que necesite una referencia al actor que se inyecta con @Named ("nombre del actor"). El enrutador configurado creará una instancia de actor cuando sea necesario.

Cuestiones relacionadas