当异步处理程序异常时,Akka.Net PreRestart不会执行

时间:2017-12-03 10:37:57

标签: c# akka task akka.net

我有以下Actor,我正在尝试重新启动并将失败的消息重新发送回actor:

public class BuildActor : ReceivePersistentActor
{
    public override string PersistenceId => "asdad3333";

    private readonly IActorRef _nextActorRef;

    public BuildActor(IActorRef nextActorRef)
    {
        _nextActorRef = nextActorRef;

        Command<Workload>(x => Build(x));

        RecoverAny(workload =>
        {
            Console.WriteLine("Recovering");
        });
    }

    public void Build(Workload Workload)
    {
        var context = Context;
        var self = Self;

        Persist(Workload, async x =>
        {
            //after this line executes
            //application goes into break mode
            //does not execute PreStart or Recover
            var workload = await BuildTask(Workload);

            _nextActorRef.Tell(workload);

            context.Stop(self);
        });
    }

    private Task<Workload> BuildTask(Workload Workload)
    {
        //works as expected if method made synchronous
        return Task.Run(() =>
        {
            //simulate exception
            if (Workload.ShowException)
            {
                throw new Exception();
            }

            return Workload;
        });
    }

    protected override void PreRestart(Exception reason, object message)
    {
        if (message is Workload workload)
        {
            Console.WriteLine("Prestart");

            workload.ShowException = false;

            Self.Tell(message);
        }
    }
}

Persist的成功处理程序内部我试图模拟抛出的异常但是在异常时应用程序进入中断模式并且不调用PreRestart挂钩。但是,如果我通过删除BuildTask使Task.Run方法同步,则​​会在异常时调用PreRestartRecover<T>方法。

我真的很感激,如果有人可以指出我应该推荐的模式以及我出错的地方。

1 个答案:

答案 0 :(得分:2)

最有可能的是,Akka.Persistence不是解决问题的好方法。

Akka.Persistence使用eventsourcing原则来存储演员的状态。在这种情况下,很少有关键点重要:

  • 您发送给演员的是命令。它描述了一项工作,你想要完成。执行该命令可能会导致进行一些实际处理,并最终导致以事件的形式持续存在演员的线性状态变化历史。
  • 在Akka.NET中,Persist方法仅用于存储事件 - 它们描述事实,已发生的事情:因此,他们不能被拒绝,他们不会失败(你在Persist回调中做的事情。)
  • 当演员在任何时间点重新开始时,它总是会尝试通过重播所有事件Persist来重新创建自己的状态,直到最后一个已知的时间点。因此,Recover方法应该只关注重放actor的状态(它可以在同一事件中多次调用)并且永远不会产生副作用(副作用的例子),这一点非常重要。正在发送电子邮件)。任何抛出的异常都意味着,演员状态不可挽回地被腐蚀,演员将被杀死。

如果您想将消息重新发送给您的演员,您可以:

  1. 在actor处理管道前放置一个可靠的消息队列(即RabbitMQ或Azure Service Bus)或log(Kafka或Event Hub)。在许多情况下,这实际上是最合理的情况。
  2. 使用来自Akka.Persistence的at-least-once delivery语义 - 但只有在某些情况下你不能使用第一个解决方案时才会使用恕我直言。
  3. 最简单且不可靠的选项(因为消息仅驻留在内存中且从不持久)是dead letter queue。每个未处理的消息都发送到那里。您可以订阅它并过滤传入的数据,以检测应该将哪些邮件再次发送给收件人。