RetryWhen和异常逻辑

时间:2018-09-24 09:36:18

标签: c# exception-handling system.reactive

我一直在考虑为简单的网络流实现基本的重试逻辑。这个想法是要处理三种类型的异常:

  • 暂时异常,应重新连接并重试
  • 永久错误连接,应传播并让进程死亡
  • 应吞咽并应完成可观察性的异常

在草绘此草图时,我最终得到类似

var retriedObservable = observable.RetryWhen(x => x.SelectMany(ex =>
{
    if (ex is ShouldCompleteException)
    {
        return Observable.Return<Exception>(null);
    }
    else if (ex is TransientNetworkException)
    {
        return Observable.Return(ex).Delay(TimeSpan.FromSeconds(1));
    }
    else
    {
        return Observable.Throw<Exception>(ex);
    }
}).TakeUntil(ex => ex == null));

编辑:此示例已高度简化,仅显示了我在代码中看到的三种典型的错误处理模式。在现实世界中,当然还有很多其他东西。

该代码有效,但看起来过于复杂,并且我不确定特殊的“ null”值是否会在某个时候碰到我。

我还尝试了.Where().Merge()的组合,但是代码很快变得不可读。

是否有一种更简洁的方法(即更规范)来完成此基本错误处理?

2 个答案:

答案 0 :(得分:1)

这会有所帮助:

var retriedObservable2 = observable.RetryWhen(exStream => Observable.Merge(
    exStream.OfType<ShouldCompleteException>().Select(_ => (Exception)null),
    exStream.NotOfType(typeof(ShouldCompleteException)).OfType<TransientNetworkException>().Delay(TimeSpan.FromSeconds(1)),
    exStream.NotOfTypes(typeof(ShouldCompleteException), typeof(TransientNetworkException)).SelectMany(e => Observable.Throw<Exception>(e))
).TakeUntil(ex => ex == null));

使用以下扩展消息:

public static class X
{
    public static IObservable<TSource> NotOfType<TSource>(this IObservable<TSource> source, Type t)
    {
        return source.Where(o => !t.IsInstanceOfType(o));
    }

    public static IObservable<TSource> NotOfTypes<TSource>(this IObservable<TSource> source, params Type[] ts)
    {
        return source.Where(o => ts.All(t => !t.IsInstanceOfType(o)));
    }
}

答案 1 :(得分:0)

已经有很棒的库,例如Polly。但是,如果您想实现自己的实现,则可以执行以下操作。

您可以创建一个RetryPolicyHandler类。同样,您可以使用接口对其进行抽象,并根据需要与任何IoC容器一起使用。通过以下实现添加类似RetryExecuteAsync的方法。

public async Task RetryExecuteAsync(Func<Task> action)
{
    int retryCount = default(int);
    int maxNumberOfRetries = 5; // Or get from settings

    while (true)
    {
        try
        {
            await action().ConfigureAwait(false);

            break;
        }
        catch (ArgumentNullException)
        {
            // Something specific about this exception
            if (++retryCount > maxNumberOfRetries)
                throw;
        }
        catch (Exception)
        { 
            if (++retryCount > maxNumberOfRetries)
                throw;
        }
    }
}

然后您可以像这样使用它。

await this.retryPolicyHandler.RetryExecuteAsync(async () =>  
{
    // Whatever code you want to retry
}).ConfigureAwait(false);

如果需要,还可以创建其他特定方法。例如,ExecuteDeadlockRetryAsync捕获SqlExceptionExecuteHttpCallRetryAsync来处理HttpRequestException并以另一种方式处理它们。