Akka DeathWatch - 查找终止原因

时间:2017-06-22 14:37:28

标签: java akka

问题:如何判断演员是否被优雅地停止(例如通过其父停止)或通过例外?

上下文:使用以下死亡计划设置我只在良好测试中获得Terminated.class消息,我明确地呼叫停止。我只期待一个Terminated.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);
    }
}

演员

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

1 个答案:

答案 0 :(得分:3)

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

  

为了在另一个actor终止时被通知(即永久停止,而不是暂时失败并重新启动),actor可以注册自己以接收另一个actor在终止时发送的Terminated消息。

here

  

一个actor的终止分两步进行:首先,actor暂停其邮箱处理并向其所有子节点发送一个停止命令,然后它继续处理来自其子节点的内部终止通知,直到最后一个消失为止,最后终止自身(调用postStop,转储邮箱,在DeathWatch上发布Terminated,告诉其主管)....

     

在actor完全停止后调用postStop()挂钩。

Terminated消息不保留给因由于异常或错误而停止actor的场景;它会在演员停止时发挥作用,包括演员“正常”停止的场景。让我们来看看测试用例中的每个场景:

  1. 没有明确主管的“好”案件MyActor自行停止,调用postStop(未被覆盖,因此{{1}没有任何反应}},并向正在观看它的演员(你的postStop演员)发送Terminated消息。

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

  3. 没有显式主管的“坏”情况:使用默认监督策略,即重启actor。重启不会触发发送DeathWatch消息。

  4. 带有明确主管的“坏”案例:主管处理Terminated,然后停止Exception,再次启动上述终止链,导致发送给观看演员的MyActor消息。

  5. 那么当一个演员停止时,如何区分“好”和“坏”的情况呢?看看logs。默认情况下,Termination在ERROR级别发生logs SupervisorStrategy次失败。

    当抛出异常时,如果您想做的不仅仅是记录异常,请考虑restarting actor而不是停止它。与停止不同,重启始终表示出现了问题(如前所述,重启是抛出异常时的默认策略)。您可以将异常后逻辑放在StoppreRestart挂钩中。

    请注意,当actor正在处理消息时抛出异常,该消息将丢失,如here所述。如果您想对该消息执行某些操作,则必须捕获异常。

    如果您有一个想要在抛出异常时通知的actor,您可以从父级的主管策略(可以抛出异常的actor的父级)中向此监视器actor发送消息。这假设父actor具有对此监视器actor的引用。如果策略是在父级内而不是在父级的伴随对象中声明的,那么策略主体可以访问抛出异常的actor(通过postRestart)。下面的sender是一个虚构的课程:

    ErrorMessage