在任务上使用 akka.net PipeTo() 进行异常处理

Exception Handling with akka.net PipeTo() on Task

参考 Akka.Net 文档,在处理异步作业时首选 PipeTo()

在处理returns Task<T>的函数时,我可以处理失败事件,没问题。

问题是,当处理一个没有 return 任何类型而只有 Task 的函数时,仍然会调用 PipeTo 函数,而不是重载其中包含失败句柄,现在显示如下:'As this task has no result, only exceptions will be piped to the recipient.'.

这是否意味着,如果我有以下代码:

public class RepositoryComponent : IRepositoryComponent
{
    private SqlSettings _sqlSettings;

    public RepositoryComponent(SqlSettings sqlSettings)
    {
        _sqlSettings = sqlSettings;
    }

    public async Task InsertJobAsync(RetryModel job)
    {
        try
        {
            await... //some logic
        }
        catch { throw; }
    }
}

我的演员:

public class RepositoryActor : ActorBase
{
    private IRepositoryComponent _repoComponent;
    private ActorSelection _retryActor;

    public RepositoryActor(IRepositoryComponent repoComponent) : base()
    {
        _repoComponent = repoComponent;
    }

    public override void Listening()
    {
        Receive<RepositoryMessages.GenericRequestNoReturn>(x => InvokeRequest(x));
        Receive<RepositoryMessages.GenericRequestWithResponseType>(x => InvokeRequestAndSendResponse(x));
    }

    private void InvokeRequest(RepositoryMessages.GenericRequestNoReturn msg)
    {
        try
        {
            //some logic with msg
            _repoComponent.InsertJobAsync(new Core.Models.RetryModel()).PipeTo(Context.Self);
        }
        catch (Exception ex)
        {
            base.ExceptionHandling(ex);
        }
    }
}

为了在上面的actor中捕获异常,我需要添加另一个接收处理程序来处理异常,例如:

Receive<Exception>(x => SomeExceptionHandler(x));

这是正确的吗?如果是这样,我的代码块周围不需要 try {} catch {} 吗?

如果您使用 PipeTo 调用异步操作,它不会阻止您当前的代码执行路径。这意味着 try/catch 语句将永远不会被执行(因为代表异步操作的任务的成功或失败将被重定向到 Self)。

有两种方法可以处理异步操作执行过程中可能发生的异常:

  1. 结合异步 lambda 使用 ReceiveAsync<> 而不是 Receive<> - 这将允许您简单地 await 完成异步操作。您可以在此上下文中自由使用 try/catch,因为它会按预期工作。请记住,这将使您的 actor non-reentrant - 这意味着,在当前异步操作完成之前,actor 不会处理任何消息。
  2. 保留 PipeTo 并在您的逻辑中添加另一个 Receive<> 处理程序 - 以防任务包装异步操作失败时,异常将被包装到消息中并重定向回 PipeTo 的目标(Self 在你的例子中)。默认情况下,以这种方式产生的异常被包装在 Status.Failure 消息中——在这种情况下,您需要在您的 actor 中添加 Receive<Status.Failure> 处理程序。您还可以指定用于处理 PipeTo 失败的自定义消息构造函数,即:_repoComponent.InsertJobAsync(input).PipeTo(Context.Self, failure: exception => new MyErrorMessage(exception))