В настоящее время я пытаюсь начать работу с 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 следует использовать только в качестве последнего средства, не так ли?
Правильный способ справиться с проблемами внутри аккских акков - это не исключение, а установление иерархии диспетчера
"Выбрасывание исключения в параллельном коде (предположим, что мы используем не связанных между собой участников), просто просто взорвет поток, который в настоящее время исполняет актер.
Невозможно узнать, что все пошло не так (кроме проверка трассировки стека). Вы ничего не можете с этим поделать.
см. Отказоустойчивость через иерархии супервизора (1.2)
* note * выше, это верно для старых версий 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)
}
}
Отключение регистрации, чтобы убедиться, что все закончилось, как предложил Виктор, немного странно. Вместо этого вы можете:
EventHandler.shutdown()
который полностью отключает всех слушателей (регистраторов), которые заставляют мир работать после исключения:
def shutdown() {
foreachListener(_.stop())
EventHandlerDispatcher.shutdown()
}
Поворот регистратора в akka.conf