2013-06-09 6 views
7

Diciamo che alcuni sono comunemente usati da altri attori del livello di servizio degli attori. Ad esempio, un servizio di registro che memorizza e recupera oggetti di dominio:Akka - Attore di servizio comune: Identify o Extension

case class DomainObject(id: UUID) 

class Registry extends akka.actor.Actor { 
    def receive: Receive = { 
    case o: DomainObject => store(o) // save or update object 

    case id: UUID => sender ! retrieve(id) // retrieve object and send it back 
    } 
} 

io non voglio passare esplicitamente esempio di tale registro in tutti i soggetti che possono usarlo. Invece di farlo, voglio che siano in grado di "localizzarlo" in qualche modo.

Per questo mi viene in mente due soluzioni:

  1. Identify messaggio: ogni attore utente Registro sa registro dei nomi attore da qualche configurazione e in grado di inviato messaggio di identificazione ad esso. Dopo AgentIdentity messaggio è ricevuto di nuovo noi siamo pronti per partire:

    val registryName = ... // some name 
    val registryId = ... // some id 
    var registry = _ 
    
    def preStart() { 
        context.actorSelection(registryName) ! Identify(registryId) 
    } 
    
    def receive: Receive = { 
        case ActorIdentity(`registryId`, ref) => registry = ref 
    } 
    

    non mi piace questo modo perché subito dopo l'attore utente inizializzazione v'è una fase in cui non sappiamo se esiste un registro nel sistema di et tutto e quindi non so se saremo mai in grado di operare o meno.

  2. Akka estensioni: Posso creare un'estensione che sarebbe:

    a. creare istanza di attore del Registro di sistema in un determinato sistema di attori all'avvio;

    b. restituisci questo attore all'utente che ne ha bisogno tramite un metodo in Extension.

    object RegistryKey extends ExtensionKey[RegistryExtension] 
    
    class RegistryExtesion(system: ExtendedActorSystem) extends RegistryKey { 
        val registry = system.actorOf(Props[Registry], "registry") 
    } 
    

La domanda è: qual è il metodo migliore e sono Akka Extesions possono essere utilizzati per questo a tutti?

+0

Credo che si intende 'context.actorSelection (RegistryName)! Identificare (registryId) '. 'actorFor' è un'altra soluzione anche se è deprecata in 2.2. – sourcedelica

+0

Sì, hai assolutamente ragione. Perdite di memoria. :) – Seigert

risposta

3

Penso che l'idea di estensione sia buona finché l'attore del registro sarà sempre nello stesso ActorSystem.

alternativa, utilizzando actorSelection (adattato da Remote Lookup):

class RegistryClient extends Actor { 
    val path = "/path/to/registry/actor" 
    context.setReceiveTimeout(3.seconds) 

    def sendIdentifyRequest(): Unit = 
    context.actorSelection(path) ! Identify(path) 

    def receive = { 
    case ActorIdentity(`path`, Some(ref)) ⇒ 
     context.setReceiveTimeout(Duration.Undefined) 
     context.become(active(ref)) 
    case ActorIdentity(`path`, None) ⇒ 
     throw new RuntimeException("Registry not found") 
    case ReceiveTimeout ⇒ sendIdentifyRequest() 
    } 

    def active(registry: ActorRef): Actor.Receive = { 
    // use the registry 
    } 
} 

questo lavoro per attori remoti o locali.

Diamo un'occhiata alla soluzione di estensione. Actors are created asynchronously. Pertanto, il costruttore dell'estensione non fallirà quando si chiama actorOf se l'attore non riesce a inizializzarsi.

Se si desidera sapere con certezza che l'attore non è riuscito a inizializzare, un modo per sapere è quello di rispondere a ask all'attore a cui risponderà e a Await. Il Await getterà uno TimeoutException se l'attore non risponde.

class RegistryExtension(system: ExtendedActorSystem) extends Extension { 
    val registry = system.actorOf(Props[Registry], "registry") 
    implicit val timeout: Timeout = Timeout(500.millis) 
    val f = registry ? "ping" // Registry should case "ping" => "pong" 
    Await.result(f, 500.millis) // Will throw a TimeoutException if registry fails 
           // to respond 
} 

Il TimeoutException otterrà gettato quando si chiama RegistryExtension(system).registry la prima volta.

+0

Sì, il registro sarà sempre nello stesso sistema degli attori che lo utilizzano. La mia preoccupazione principale è il modo in cui Extension si comporta con tolleranza agli errori? Se con qualsiasi mezzo l'estensione non sarà in grado di creare un'istanza di un attore di registro, si verificherà un'eccezione di attore utente durante l'invocazione del metodo .registry Extension? Oppure si verificherà un errore su Extension init? – Seigert

+0

risposta aggiornata per includere la creazione di un attore del registro fallita. – sourcedelica

+0

Ho deciso di utilizzare un approccio misto nel sistema: 'Identify' dove è richiesto un basso accoppiamento o remoting ed estensioni per gli attori che dovrebbero essere nel sistema affinché funzioni. – Seigert

3

E la Cake Pattern o una libreria Iniezione di dipendenza come subcut.

Derek Wyatt menzioni DI nel suo libro 'Akka Concurrency' invece di usare troppo actorFor per cercare gli attori: http://www.artima.com/forums/flat.jsp?forum=289&thread=347118

+0

Bene, vorrei mantenere le dipendenze del progetto al minimo per ora. Quindi, immagino, un'altra libertà solo per DI di attori per un altro attore è fuori dai limiti. – Seigert

+1

Il pattern di torta non richiede ulteriori dipendenze. Solo core alla Scala. – theon

+0

Questo è un caso classico in cui Dipendenza iniezione è lo strumento corretto. Non devi fare nulla di pesante come import Spring e iniziare a usare XML ovunque, basta che l'attore del Registro di sistema sia un parametro costruttore per tutti gli attori che dipendono da esso e insegui queste modifiche finché non ti trovi a crearne una all'avvio dell'applicazione e passando per l'intera app. – Rich