java - Akka DeathWatch - 查找终止原因

标签 java akka

问题:我怎样才能知道一个 Actor 是被正常停止(例如通过其父进程停止)还是通过异常停止?

上下文:通过以下死亡观察设置,我仅在良好的测试中收到 Termulated.class 消息,其中我显式调用 stop。我预计只有在糟糕的情况下才会出现 Termated.class 消息。使用 SupervisorStrategy 来阻止抛出异常的子进程不会产生任何影响,因为这会导致良好测试的行为。而且我找不到一种方法来确定它是否是由异常引起的。

我的测试设置如下:

死亡观察

public class DeathWatch extends AbstractActor {

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .matchAny(this::logTerminated)
                .build();
    }

    private <P> void logTerminated(final P p) {
        log.info("terminated: {}", p);
    }
}

Actor

public class MyActor extends AbstractActor {

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .matchEquals("good", s -> { getContext().stop(self()); })
                .matchEquals("bad", s -> { throw new Exception("baaaad"); })
                .build();
    }
}

测试

public class Test {

    private TestActorRef<Actor> actor;

    @Before
    public void setUp() throws Exception {
        actor = TestActorRef.create(ActorSystem.create(), Props.create(MyActor.class), "actor");
        TestActorRef.create(ActorSystem.create(), Props.create(DeathWatch.class),"deathwatch").watch(actor);
    }

    @Test
    public void good() throws Exception {
        actor.tell("good", ActorRef.noSender());
    }

    @Test
    public void bad() throws Exception {
        actor.tell("bad", ActorRef.noSender());
    }
}

更新:添加以下主管会导致第二次记录“终止”,但不会产生进一步的上下文信息。

public class Supervisor extends AbstractActor {

    private final ActorRef child;

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(String.class, s -> child.tell(s, getSelf()))
                .build();
    }

    @Override
    public SupervisorStrategy supervisorStrategy() {
        return new OneForOneStrategy(DeciderBuilder.match(Exception.class, e -> stop()).build());
    }
}

最佳答案

Termied 消息的行为符合预期。来自 documentation :

In order to be notified when another actor terminates (i.e. stops permanently, not temporary failure and restart), an actor may register itself for reception of the Terminated message dispatched by the other actor upon termination.

here :

Termination of an actor proceeds in two steps: first the actor suspends its mailbox processing and sends a stop command to all its children, then it keeps processing the internal termination notifications from its children until the last one is gone, finally terminating itself (invoking postStop, dumping mailbox, publishing Terminated on the DeathWatch, telling its supervisor)....

The postStop() hook is invoked after an actor is fully stopped.

Termated 消息不是为 Actor 由于异常或错误而停止的情况保留的;每当 Actor 停止时它就会发挥作用,包括 Actor “正常”停止的场景。让我们来看看测试用例中的每个场景:

  1. 没有显式主管的“好”情况:MyActor自行停止,调用postStop(它没有被覆盖,所以postStop 中没有任何反应),并向正在观看它的参与者(您的 DeathWatch actor)发送一条 Termulated 消息。

  2. 有明确主管的“好”案例:与 1 相同。

  3. 没有明确监督者的“坏”情况:使用默认的监督策略,即重新启动参与者。重新启动不会触发Termied消息的发送。

  4. 具有显式主管的“坏”情况:主管处理异常,然后停止MyActor,再次启动终止上述链,导致发送到观看 Actor 的终止消息。

那么,当 Actor 停止时,如何区分“好”和“坏”的情况呢?看logs 。默认情况下,SupervisorStrategy logs 停止错误级别的故障。​​

当抛出异常时,如果您想做的不仅仅是记录异常,请考虑 restarting Actor 而不是阻止它。与停止不同,重新启动总是表明出现了问题(如前所述,重新启动是引发异常时的默认策略)。您可以将异常后逻辑放置在 preRestartpostRestart Hook 内。

请注意,当参与者处理消息时引发异常时,该消息将丢失,如 here 中所述。 。如果您想对该消息执行某些操作,则必须捕获异常。

如果您想要在抛出异常时通知某个 Actor,则可以从父级的主管策略(可以抛出异常的 Actor 的父级)内向该监视 Actor 发送消息。这假设父 Actor 具有对此监视器 Actor 的引用。如果策略是在父级内部声明的,而不是在父级的伴生对象中声明的,则策略的主体可以访问引发异常的参与者(通过 sender)。下面的 ErrorMessage 是一个虚构的类:

override val supervisorStrategy =
  OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
    case t: Throwable =>
      val problemActor = sender
      monitorActor ! ErrorMessage(t, problemActor)
      Stop
}

关于java - Akka DeathWatch - 查找终止原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44702520/

相关文章:

scala - 使用 Akka/Akka Streams/Akka HTTP 时出现 Akka 版本冲突

scala - 考虑到结果无法作为来自同一消息的响应,如何通过询问 Akka Actor 来获得 Promise(或 Future)

java 8 Docker 错误指定的 VM 选项 'InitialRAMPercentage=XX'

Java Swing 动画看起来不稳定。如何让它看起来专业?

java - 何时使用参与者而不是消息传递解决方案,例如 WebSphere MQ 或 Tibco Rendezvous?

scala - 如何禁用 Actor 恢复?

java - 如何将 Play 应用程序作为单个 JAR 文件运行?

java - Logback 中基于时间的文件夹删除

java - 多个自动调整大小的 TextView 只有一个大小

java - 为什么我的 while 循环在 PaintComponent 中不起作用?