web-dev-qa-db-ja.com

例外がスローされた場合、Akkaアクターが終了しない

私は現在Akkaを使い始めようとしていますが、奇妙な問題に直面しています。私のアクターには次のコードがあります。

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

そして、これは私が私の労働者を始める方法です:

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

そして、これは私がすべてをシャットダウンする方法です:

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

次に、n> 0(例外はスローされません)でワーカーメッセージを送信すると、すべてが正常に動作し、アプリケーションが適切にシャットダウンします。ただし、例外が発生する単一のメッセージを送信するとすぐに、実行中のアクターがまだ存在しているためにアプリケーションが終了しませんが、それがどこから来ているのかわかりません。

それが役立つ場合、これは問題のスレッドのスタックです:

  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:終了していないスレッドはワーカースレッドではありません。postStopコールバックを追加したため、すべてのスレッドが適切に停止します。

PPS:Actors.registry.shutdownAll問題を回避しますが、shutdownAllは最後の手段としてのみ使用するべきだと思いますか?

73
fresskoma

Akkaアクター内の問題を処理する適切な方法は、例外をスローすることではなく、スーパーバイザー階層を設定することです

「並行コードで例外をスローすると(リンクされていないアクターを使用しているとしましょう)、現在アクターを実行しているスレッドを単純に爆破します。

(スタックトレースの検査を除いて)問題が発生したことを確認する方法はありません。それについてあなたができることは何もない。」

を参照してください。スーパーバイザ階層によるフォールトトレランス(1.2)

*注意*上記はAkkaの古いバージョン(1.2)に当てはまります。新しいバージョン(2.2など)では、スーパーバイザー階層を設定しますが、子プロセスによってスローされた例外をトラップします。例えば.

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

そしてスーパーバイザーで:

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 _: ArithmeticException      ⇒ Resume
        case _: NullPointerException     ⇒ Restart
        case _: IllegalArgumentException ⇒ Stop
        case _: Exception                ⇒ Escalate
      }

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

を参照してください。スーパーバイザ階層によるフォールトトレランス(2.2)

21

Viktorによって提案されているように、ログをオフにしてすべてが確実に終了するようにするのは少し奇妙です。代わりにできることは:

EventHandler.shutdown()

これにより、例外の後でワールドを実行し続けるすべての(ロガー)リスナーが完全にシャットダウンされます。

def shutdown() {
  foreachListener(_.stop())
  EventHandlerDispatcher.shutdown()
}
2
tolitius