如果引发异常,Akka Actor不会终止


73

我目前正在尝试开始使用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(不引发异常)发送工作程序消息,则一切正常,并且应用程序正常关闭。但是,一旦我向它发送一条导致异常的消息,该应用程序就不会终止,因为仍然有一个actor正在运行,但是我不知道它来自何处。

如果有帮助,这是有问题的线程的堆栈:

  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
@ViktorKlang:但是为什么它仍然存在,如何正确停止它?:)
fresskoma 2011年

8
什么时候应该停止?请参阅此处的关闭方法:akka.io/api/akka/1.1.2/#akka.event.EventHandler$
维克多·巴生

9
在将默认事件处理程序用于Akka的“记录”时,可以启动它。它可以在akka.conf中配置
Viktor Klang

1
@Pablo Fernandez:但是为什么我必须禁用日志记录才能使我的应用程序正确终止?恕我直言,这更多的是替代方法,而不是解决方案...
fresskoma

1
@ x3ro:偶然发现了这个(寻找非终止的原因时)。是否能够验证剩下的演员确实是EventHandler?(对我来说是。)听起来很有趣,所以我检查了代码:对您来说,原因似乎是引发异常将导致EventHandler.error被调用(以打印stacktrace?)....我想您将不得不还会在EventHandler上扔一个PoisonPill来关闭它,一些Akka专家可能有更好的解决方案。
subsub

Answers:


22

处理Akka参与者内部问题的正确方法不是抛出异常,而是设置主管层次结构

“在并发代码中引发异常(假设我们使用的是非链接角色),只会简单地炸毁当前执行该角色的线程。

除了检查堆栈跟踪信息之外,没有其他方法可以找出问题所在。您对此无能为力。”

看到 通过主管层次结构的容错(1.2)

*注意*以上内容适用于Akka的旧版本(1.2)在较新的版本(例如2.2)中,您仍将设置主管级别,但是它将捕获子进程抛出的异常。例如

class Child extends Actor {
    var state = 0
    def receive = {
      case ex: Exceptionthrow 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 _: ArithmeticExceptionResume
        case _: NullPointerExceptionRestart
        case _: IllegalArgumentExceptionStop
        case _: ExceptionEscalate
      }

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

请参见通过主管层次结构的容错(2.2)


1
我对异常也有同样的问题:抛出异常,显示出来,但是actor处理了更多消息,好像没有发生异常。因此,我切换到主管层次结构。之所以精疲力尽,仅仅是因为无法以其他方式处理异常。
Peter Schmitz

最新版本的Akka中似乎并非如此,在Akka中,抛出异常正是主管人员所监督的。
nornagon

2

根据Viktor的建议,关闭日志记录以确保一切终止,这有点奇怪。您可以做的是:

EventHandler.shutdown()

彻底关闭所有(记录器)侦听器,这些侦听器在发生异常后使世界保持运行:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.