Akka Actor wird nicht beendet, wenn eine Ausnahme ausgelöst wird

73

Ich versuche gerade mit Akka anzufangen und stehe vor einem seltsamen Problem. Ich habe den folgenden Code für meinen Schauspieler:

class AkkaWorkerFT extends Actor {
  def receive = {
    case Work(n, c) if n < 0 => throw new Exception("Negative number")
    case Work(n, c) => self reply n.isProbablePrime(c);
  }
}

Und so starte ich meine Arbeiter:

val workers = Vector.fill(nrOfWorkers)(actorOf[AkkaWorkerFT].start());
val router = Routing.loadBalancerActor(SmallestMailboxFirstIterator(workers)).start()

Und so schalte ich alles aus:

futures.foreach( _.await )
router ! Broadcast(PoisonPill)
router ! PoisonPill

Wenn ich jetzt die Worker-Nachrichten mit n> 0 sende (es wird keine Ausnahme ausgelöst), funktioniert alles einwandfrei und die Anwendung wird ordnungsgemäß heruntergefahren. Sobald ich jedoch eine einzelne Nachricht sende, die zu einer Ausnahme führt, wird die Anwendung nicht beendet, da noch ein Akteur ausgeführt wird, aber ich kann nicht herausfinden, woher sie stammt.

Falls es hilft, ist dies der Stapel des fraglichen Threads:

  Thread [akka:event-driven:dispatcher:event:handler-6] (Suspended) 
    Unsafe.park(boolean, long) line: not available [native method]  
    LockSupport.park(Object) line: 158  
    AbstractQueuedSynchronizer$ConditionObject.await() line: 1987   
    LinkedBlockingQueue<E>.take() line: 399 
    ThreadPoolExecutor.getTask() line: 947  
    ThreadPoolExecutor$Worker.run() line: 907   
    MonitorableThread(Thread).run() line: 680   
    MonitorableThread.run() line: 182   

PS: Der Thread, der nicht beendet wird, ist keiner der Arbeitsthreads, da ich einen PostStop-Rückruf hinzugefügt habe. Jeder von ihnen stoppt ordnungsgemäß.

PPS: Actors.registry.shutdownAllUmgehung des Problems, aber ich denke, shutdownAll sollte nur als letztes Mittel verwendet werden, nicht wahr?

Fresskoma
quelle
1
@ViktorKlang: Aber warum bleibt es und wie stoppe ich es richtig? :)
Fresskoma
8
Wann sollte es gestoppt werden? Siehe die Abschaltmethode hier: akka.io/api/akka/1.1.2/#akka.event.EventHandler$
Viktor Klang
9
Sie starten es, wenn Sie den Standardereignishandler für die "Protokollierung" von Akka verwenden. Es ist konfigurierbar in der akka.conf
Viktor Klang
1
@Pablo Fernandez: Aber warum sollte ich die Protokollierung deaktivieren müssen, damit meine Anwendung ordnungsgemäß beendet wird? IMHO, das ist eher eine Problemumgehung als eine Lösung ...
Fresskoma
1
@ x3ro: Bin gerade darüber gestolpert (bei der Suche nach dem Grund für die Nichtbeendigung). Konnten Sie überprüfen, ob der verbleibende Schauspieler tatsächlich der ist EventHandler? (Für mich war es das.) Es klang interessant, also habe ich den Code überprüft: Für Sie scheint der Grund zu sein, dass das Auslösen einer Ausnahme dazu führt EventHandler.error, dass aufgerufen wird (um den Stacktrace zu drucken?) .... Ich denke, Sie müssen Werfen Sie auch eine PoisonPill auf den EventHandler, um ihn herunterzufahren. Einige Akka-Experten haben möglicherweise eine bessere (schönere) Lösung.
Subsub

Antworten:

22

Der richtige Weg, um Probleme innerhalb von Akka-Akteuren zu behandeln, besteht nicht darin, eine Ausnahme auszulösen, sondern Supervisor-Hierarchien festzulegen

"Wenn Sie eine Ausnahme in gleichzeitigem Code auslösen (nehmen wir an, wir verwenden nicht verknüpfte Akteure), wird der Thread, der den Akteur derzeit ausführt, einfach in die Luft gesprengt.

Es gibt keine Möglichkeit herauszufinden, dass ein Fehler aufgetreten ist (abgesehen von der Überprüfung der Stapelverfolgung). Sie können nichts dagegen tun. "

siehe Fehlertoleranz durch Supervisor-Hierarchien (1.2)

* Hinweis * Dies gilt für alte Versionen von Akka (1.2). In neueren Versionen (z. B. 2.2) haben Sie immer noch eine Supervisor-Hierarchie festgelegt, aber es werden Ausnahmen abgefangen, die von untergeordneten Prozessen ausgelöst werden. z.B

class Child extends Actor {
    var state = 0
    def receive = {
      case ex: Exceptionthrow ex
      case x: Int        ⇒ state = x
      case "get"         ⇒ sender ! state
    }
  }

und im Supervisor:

class Supervisor extends Actor {
    import akka.actor.OneForOneStrategy
    import akka.actor.SupervisorStrategy._
    import scala.concurrent.duration._

    override val supervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
        case _: ArithmeticExceptionResume
        case _: NullPointerExceptionRestart
        case _: IllegalArgumentExceptionStop
        case _: ExceptionEscalate
      }

    def receive = {
      case p: Props ⇒ sender ! context.actorOf(p)
    }
  }

siehe Fehlertoleranz durch Supervisor-Hierarchien (2.2)

Arnon Rotem-Gal-Oz
quelle
1
Ich hatte das gleiche Problem mit Ausnahmen: Ausgelöst, angezeigt, aber der Schauspieler verarbeitete weitere Nachrichten, als ob die Ausnahme nicht aufgetreten wäre. Also wechselte ich zu Supervisor-Hierarchien. Erschöpfend, nur weil Ausnahmen nicht anders behandelt werden können.
Peter Schmitz
Dies scheint in neueren Versionen von Akka nicht der Fall zu sein, in denen das Auslösen von Ausnahmen genau das ist, was Supervisoren überwachen.
Nornagon
2

Das Ausschalten der Protokollierung, um sicherzustellen, dass die Dinge beendet werden, wie von Viktor vorgeschlagen, ist etwas seltsam. Was Sie stattdessen tun können, ist:

EventHandler.shutdown()

Dadurch werden alle (Logger-) Listener sauber heruntergefahren, die die Welt nach der Ausnahme am Laufen halten:

def shutdown() {
  foreachListener(_.stop())
  EventHandlerDispatcher.shutdown()
}
Tolitius
quelle
-2

Schalten Sie den Logger in der akka.conf

andersoyvind
quelle