【问题标题】:Akka DeathWatch - Find reason for terminationAkka DeathWatch - 找到终止的原因
【发布时间】:2017-06-22 14:37:27
【问题描述】:

问题:我怎样才能知道演员是被优雅地停止(例如通过其父停止)还是通过异常停止?

上下文:通过以下死亡观察设置,我只在良好测试中收到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());
    }
}

【问题讨论】:

    标签: java akka


    【解决方案1】:

    Terminated 消息按预期运行。来自documentation

    为了在另一个参与者终止时得到通知(即永久停止,而不是暂时失败并重新启动),参与者可以注册自己以接收另一个参与者在终止时分派的Terminated 消息。

    还有here

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

    在演员完全停止后调用postStop() 钩子。

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

    1. 没有明确监督者的“好”案例MyActor 停止自己,调用postStop(它没有被覆盖,所以在postStop 中什么都没有发生),并发送一个@987654336给正在观看它的演员的@消息(你的DeathWatch演员)。

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

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

    4. 带有显式监管者的“坏”情况:监管者处理Exception,然后停止MyActor,再次启动上述终止链,导致Termination消息发送到观看演员。

    那么当一个演员被阻止时,如何区分“好”和“坏”的情况呢?看看logsSupervisorStrategy,默认情况下,logs Stop 在 ERROR 级别失败。

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

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

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

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

    【讨论】:

    • 感谢您的澄清。 “看日志。”遗憾的是,这还不够,因为如果抛出异常,我们需要进行错误处理。我认为这样做的唯一方法是使用进一步升级到专门的异常处理主管。
    猜你喜欢
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多