2013-08-02 16 views
18

Mam obiekt java, który nie jest aktorem, który wybiera aktorów z systemu aktor z actorSelection (Path)). Możliwe, że wybrany aktor nie istnieje w systemie.Jak mogę sprawdzić, czy istnieje aktor Akka (akka 2.2)?

W Java Api ask() nie istnieje dla ActorSelection, więc nie mogę wysyłać i identyfikować wiadomości do wyboru aktora i używać nadawcy odpowiedzi.

Próbowałem rozwiązać problem, wysyłając wiadomość do aktora i tak poprzez wybór aktora, a następnie reagując na ogłoszenie. Ale nie dostaję żadnych listów.

W jaki sposób mogę sprawdzić w ActorSelection, czy aktor żyje, czy nie istnieje?

ActorSystem system = ActorSystem.create("test"); 

//create test actor 
system.actorOf(Props.create(TestActor.class), "testActor"); 

//add dead letter listener to the system 
ActorRef eventBusActor = asys.actorOf(Props.create(EventBusActor.class), "eventbusactor"); 
system.eventStream().subscribe(eventBusActor, DeadLetter.class); 


//This works. The test actor receives the message  
ActorSelection a1 = asys.actorSelection("/user/testActor"); 
a1.tell("hello", ActorRef.noSender()); 

//This does not work and does not send dead letters  
ActorSelection a2 = asys.actorSelection("/user/doesnotexist"); 
a2.tell("hello", ActorRef.noSender()); 

//Does not compile, because ask needs an ActorRef as first argument 
ActorSelection a3 = asys.actorSelection("/user/test"); 
Future f = Patterns.ask(a3, new Identify(), 1000); 
+0

Oops, że jako nadzór, dzięki za wskazanie go: https://www.assembla.com/spaces/akka/simple_planner#/ticket: 3532 –

Odpowiedz

12

Wygląda na to, że Akka pomija wsparcie dla ActorSelection w aplecie Java dla ask. Grałem trochę z kodem i znalazłem coś, co działa. Sprawdź, czy ten kod działa:

import java.util.concurrent.TimeUnit; 

import scala.concurrent.Await; 
import scala.concurrent.Future; 

import akka.actor.ActorIdentity; 
import akka.actor.ActorRef; 
import akka.actor.ActorSelection; 
import akka.actor.ActorSystem; 
import akka.actor.Identify; 
import akka.actor.Props; 
import akka.pattern.AskableActorSelection; 
import akka.util.Timeout; 

public class AskTest { 

    public static void main(String[] args) throws Exception{ 
    ActorSystem sys = ActorSystem.apply("test"); 
    sys.actorOf(Props.create(TestActor.class), "mytest"); 

    ActorSelection sel = sys.actorSelection("/user/mytest"); 

    Timeout t = new Timeout(5, TimeUnit.SECONDS); 
    AskableActorSelection asker = new AskableActorSelection(sel); 
    Future<Object> fut = asker.ask(new Identify(1), t); 
    ActorIdentity ident = (ActorIdentity)Await.result(fut, t.duration()); 
    ActorRef ref = ident.getRef(); 
    System.out.println(ref == null); 
    } 
} 

Po prostu przyjrzałem się, w jaki sposób obsługa zapytań scala działała i podłączała się do niej poprzez Javę. To działało dla mnie; Mam nadzieję, że to działa dla ciebie.

5

Akka zapewnia funkcjonalność, aby uzyskać ActorRef z ActorSelection stosując specjalną wiadomość Identify. Nie musisz używać ask() dla tej wiadomości. Po prostu prześlij wiadomość Identify do ActorSelection i posłuchaj wiadomości o numerze ActorIdentity, która zostanie Ci przekazana. Jest przykładem dla dokładnie to w docs Akka: Identifying Actors via Actor Selection (Java)

Kod ten jest przenoszony z przykładu i modyfikowanych:

final String identifyId = "1"; 

@Override 
public void onReceive(Object message) { 
    if (message instanceof ActorIdentity) { 
     ActorIdentity identity = (ActorIdentity) message; 
     if (identity.correlationId().equals(identifyId)) { 
      ActorRef ref = identity.getRef(); 
      if (ref == null) 
       // Actor does not exist 
      else { 
       // Actor does exist 
      } 
     } 
    } 
} 

Jest też bardzo ładne graphic, który pokazuje relacje między ActorPath, ActorSelection i Cykl życia aktora w dokumentach.

+0

Muszę zidentyfikować aktora z normalnego obiektu java, a nie od innego aktora, więc nie mogę użyć twojej rady. Niestety nie można zmienić tego obiektu w aktora. – schrums

+0

Ah Widzę, przeoczyłem ten przymus. ;) Może mógłbyś odrodzić małego pomocnika-aktora w twoim obiekcie Javy w jednym celu wysyłania i odbierania wiadomości Identify. –

+0

Kroki będą następujące: 1) Wciel się w pomocnika-aktora (następnie użyj w tym celu ActorRef), 2) wyślij mu wiadomość za pomocą ask() zawierającą wszystkie informacje dotyczące aktora, który chcesz przetestować 3) zwróć wynik z Aktor, kiedy przybyli. To może działać ...;) –

21

Niedawno znalazłem metodę ActorSelection.resolveOne:

val name = "myActor" 
implicit val timeout = 5000 // Timeout for the resolveOne call 
system.actorSelection(name).resolveOne().onComplete { 
    case Success(actor) => actor ! message 

    case Failure(ex) => 
    val actor = system.actorOf(Props(classOf[ActorClass]), name) 
    actor ! message 
} 

Jeden problem mam nadal bada to, że metoda ta jest zdefiniowana w których można nazwać jednocześnie (od innych podmiotów). Dlatego możliwe jest uzyskanie warunku wyścigu, w którym spróbujesz utworzyć aktora dwa razy, jeśli wywołanie resolveOne nie powiedzie się, ponieważ aktor jest wciąż tworzony. To może, ale nie musi, być problemem dla twojego przypadku użycia.

+1

Domyślam się, że jeśli próbujesz uzyskać dostęp do akcji podrzędnej, możesz użyć metody 'def child (name: String): Option [ActorRef]' w kontekście aktora. Powinien pomóc w rozwiązaniu problemu współbieżności. Zobacz także: http://stackoverflow.com/questions/16268333/get-existing-lub-create-new-akka-actor – skytteren

3

Jak inne odpowiedzi zauważ, ActorSelection.resolveOne() obsługuje to.

Jedno ostrzeżenie: pod maską działa to poprzez wysłanie wiadomości do aktora, o którym mowa. Oznacza to, że jeśli ten aktor jest zajęty, nie odpowie, a to się nie powiedzie (z przekroczeniem limitu czasu).

W czystej-najlepszej praktyce Akka, jest to prawdopodobnie przypadek narożny. W bardziej mieszanej, normalnej konfiguracji Java/Akka, łatwo jest się wkurzyć. W szczególności kod w wątku aktora nie może znaleźć odniesienia do tego aktora.

0

Korzystanie z wersji 2.3.4

Niektóre przykład Scala, może pomóc

val zed2 = Akka.system().actorSelection("path") 
    val fs:FiniteDuration = (100).millis 

    val x = zed2.resolveOne(fs).value 
    if (x.isDefined){ 
    println(x.get.isFailure) 
    }