簡體   English   中英

Akka.net - 如何在停止之前等待子actor處理所有未決消息

[英]Akka.net - How to wait child actor to process all pending messages prior to stop

我們有一個名為 A 的集群分片 Actor,它有多個子 Actor,每個實體模式都使用子 Actor 創建,如下所示。 當我們告訴演員 B 給 D 的 100 條消息,演員 D 需要 500 毫秒來處理每條消息,同時,當我們使用 Context.Parent.Tell 將毒丸發送給演員 A 時(new Passivate (PoisonPill.Instance )); 它立即停止所有子actor,包括actor D,而不處理待處理的消息。

    A
    |
    B    
   / \
  C   D

有沒有辦法等待演員 D 處理所有消息?

https://stackoverflow.com/a/70286526/377476是一個好的開始; 您將需要自定義關閉消息。 當父actor終止時,它的子actor通過/system消息自動殺死,這些消息取代了隊列中任何未處理的/user消息。

因此,您需要做的是確保他們的所有/user消息都在父級終止之前得到處理。 使用GracefulStop擴展方法和您的自定義停止消息有一種簡單的方法:

public sealed class ActorA : ReceiveActor{
    private IActorRef _actorB;  
    
    private readonly ILoggingAdapter _log = Context.GetLogger();
    
    public ActorA(){
        Receive<StartWork>(w => {
            foreach(var i in Enumerable.Range(0, w.WorkCount)){
                _actorB.Tell(i);
            }
        });
        
        ReceiveAsync<MyStopMessage>(async _ => {
            _log.Info("Begin shutdown");
            
            // stop child actor B with the same custom message
            await _actorB.GracefulStop(TimeSpan.FromSeconds(10), _);
            
            // shut ourselves down after child is done
            Context.Stop(Self);
        });
    }
    
    protected override void PreStart(){
        _actorB = Context.ActorOf(Props.Create(() => new ActorB()), "b");
    }
}

public sealed class ActorB : ReceiveActor{
    private IActorRef _actorC;
    private IActorRef _actorD;
    
    private readonly ILoggingAdapter _log = Context.GetLogger();
    
    public ActorB(){
        Receive<int>(i => {
            _actorC.Tell(i);
            _actorD.Tell(i);
        });
        
        ReceiveAsync<MyStopMessage>(async _ => {
            
            _log.Info("Begin shutdown");
            
            // stop both actors in parallel
            var stopC = _actorC.GracefulStop(TimeSpan.FromSeconds(10));
            var stopD = _actorD.GracefulStop(TimeSpan.FromSeconds(10));
            
            // compose stop Tasks
            var bothStopped = Task.WhenAll(stopC, stopD);
            await bothStopped;
            
            // shut ourselves down immediately
            Context.Stop(Self);
        });
    }
    
    protected override void PreStart(){
        var workerProps = Props.Create(() => new WorkerActor());
        _actorC = Context.ActorOf(workerProps, "c");
        _actorD = Context.ActorOf(workerProps, "d");
    }
}

public sealed class WorkerActor : ReceiveActor {
    private readonly ILoggingAdapter _log = Context.GetLogger();
    
    public WorkerActor(){
        ReceiveAsync<int>(async i => {
            await Task.Delay(10);
            _log.Info("Received {0}", i);
        });
    }
}

我在這里創建了此示例的可運行版本: https://dotnetfiddle.net/xiGyWM - 您會看到在示例開始后不久收到了MyStopMessage ,但C 和 D 已經完成工作之后。 在此場景中任何參與者終止之前,所有這些工作都已完成。

您可以定義自己的停止消息,並讓參與者使用Context.Stop(Self)來處理它,而不是發送PoisonPill - 這是一條系統消息,因此比傳統消息具有更高的優先級。

class MyShardedActor : ReceiveActor {
    public MyShardedActor() {
        Receive<MyStopMessage>(_ => Context.Stop(Self));
    }
}

您可以使用ClusterSharding.Start方法重載注冊您的自定義消息,以與集群自行觸發的鈍化調用一起使用,該方法采用handOffMessage參數,將在Passivate請求而不是PoisonPill中發送。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM