私は現在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は最後の手段としてのみ使用するべきだと思いますか?
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)
}
}
Viktorによって提案されているように、ログをオフにしてすべてが確実に終了するようにするのは少し奇妙です。代わりにできることは:
EventHandler.shutdown()
これにより、例外の後でワールドを実行し続けるすべての(ロガー)リスナーが完全にシャットダウンされます。
def shutdown() {
foreachListener(_.stop())
EventHandlerDispatcher.shutdown()
}