问题:如何判断演员是否被优雅地停止(例如通过其父停止)或通过例外?
上下文:使用以下死亡计划设置我只在良好测试中获得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());
}
}
答案 0 :(得分:3)
Terminated
消息的行为符合预期。来自documentation:
为了在另一个actor终止时被通知(即永久停止,而不是暂时失败并重新启动),actor可以注册自己以接收另一个actor在终止时发送的
Terminated
消息。
here:
一个actor的终止分两步进行:首先,actor暂停其邮箱处理并向其所有子节点发送一个停止命令,然后它继续处理来自其子节点的内部终止通知,直到最后一个消失为止,最后终止自身(调用
postStop
,转储邮箱,在DeathWatch上发布Terminated
,告诉其主管)....在actor完全停止后调用
postStop()
挂钩。
Terminated
消息不保留给因由于异常或错误而停止actor的场景;它会在演员停止时发挥作用,包括演员“正常”停止的场景。让我们来看看测试用例中的每个场景:
没有明确主管的“好”案件:MyActor
自行停止,调用postStop
(未被覆盖,因此{{1}没有任何反应}},并向正在观看它的演员(你的postStop
演员)发送Terminated
消息。
明确主管的“好”案例:与1相同。
没有显式主管的“坏”情况:使用默认监督策略,即重启actor。重启不会触发发送DeathWatch
消息。
带有明确主管的“坏”案例:主管处理Terminated
,然后停止Exception
,再次启动上述终止链,导致发送给观看演员的MyActor
消息。
那么当一个演员停止时,如何区分“好”和“坏”的情况呢?看看logs。默认情况下,Termination
在ERROR级别发生logs SupervisorStrategy
次失败。
当抛出异常时,如果您想做的不仅仅是记录异常,请考虑restarting actor而不是停止它。与停止不同,重启始终表示出现了问题(如前所述,重启是抛出异常时的默认策略)。您可以将异常后逻辑放在Stop
或preRestart
挂钩中。
请注意,当actor正在处理消息时抛出异常,该消息将丢失,如here所述。如果您想对该消息执行某些操作,则必须捕获异常。
如果您有一个想要在抛出异常时通知的actor,您可以从父级的主管策略(可以抛出异常的actor的父级)中向此监视器actor发送消息。这假设父actor具有对此监视器actor的引用。如果策略是在父级内而不是在父级的伴随对象中声明的,那么策略主体可以访问抛出异常的actor(通过postRestart
)。下面的sender
是一个虚构的课程:
ErrorMessage