2017-06-22 45 views
2

問題:如何確定演員是否優雅地停止(例如,通過其父節點停止)還是通過異常停止?Akka DeathWatch - 找到終止的原因

語境:用以下臨終看護的設置我只在良好的測試中,我明確要求停止得到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()); 
    } 
} 

回答

2

Terminated消息的行爲與預期相同。從documentation

爲了通知當另一個演員終止(即停止永久地,不是暫時的故障和重新啓動),演員可以用於通過在終止時的其他演員分派的Terminated消息的接收寄存器本身。

而且here

在兩個步驟中的演員進入的終止:第一演員暫停其郵箱處理,併發送一個停止命令到它的所有孩子,那麼它使從處理所述內部終止通知它的孩子,直到最後一個走了,最後終止本身(調用postStop,傾銷郵箱,在臨終看護髮布Terminated,告訴其主管)....

postStop()鉤子之後調用一個演員完全停下來。

Terminated消息不保留給演員由於異常或錯誤而停止的場景;它在演員停止時發揮作用,包括演員正常停止的情景。「讓我們通過每一個場景在你的測試用例:

  1. ‘沒有明確的主管好’的情況:MyActor停止本身,調用postStop(未覆蓋,所以什麼也沒有發生在postStop),和發送Terminated消息是在看它(你DeathWatch演員)演員

  2. 「好」的情況下,用顯式的主管:同1

  3. 沒有明確監督員的「壞」情況:使用默認監督策略,即重新啓動參與者。重新啓動不會觸發發送Terminated消息。

  4. 「壞」的情況下,有一個明確的主管:主管處理Exception,然後停止MyActor,再次啓動上述終止鏈,導致發送到觀看演員一個Termination消息。

那麼,當一個演員被停止時,如何區分「好」和「壞」情況?看看logs。默認情況下,SupervisorStrategylogsStop故障位於ERROR級別。

當拋出異常時,如果您不想記錄異常,請考慮restarting這個actor,而不是停止它。與停止不同,重新啓動總是表明出現了問題(如前所述,重新啓動是拋出異常時的默認策略)。您可以將異常後邏輯置於preRestartpostRestart鉤子內。

請注意,如果在actor正在處理消息時拋出異常,那麼該消息將丟失,如here所述。如果你想用這個信息做點什麼,你必須去捕捉這個例外。

如果您有一位您希望在拋出異常時通知的演員,您可以從父母的主管策略(可以拋出異常的演員的父母)內向該監視器演員發送消息。這假設父代演員有一個對這個監視器參與者的引用。如果策略在父級內而不在父級的伴侶對象中聲明,則策略的主體可以訪問發生異常的參與者(通過sender)。 ErrorMessage下面是一個化妝類:

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

謝謝你的澄清。 「看看日誌。」可悲是不夠的,因爲我們希望在拋出異常時進行錯誤處理。我認爲唯一的辦法就是進一步升級到專門的異常處理主管。 – michaelbahr