RetryWhen и логика исключения - PullRequest
       6

RetryWhen и логика исключения

0 голосов
/ 24 сентября 2018

Я пытался реализовать базовую логику повторов для простого сетевого потока.Идея состоит в том, что есть три типа исключений, которые нужно обработать:

  • Переходные исключения, следует повторно подключиться и повторить попытку
  • Соединение с постоянной ошибкой, должно распространиться и позволить процессу умереть
  • Исключения, которые следует проглотить и завершить наблюдаемое

При создании эскиза я получаю что-то вроде

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));

Редактировать: этот пример сильно упрощен, показывая только три типичных шаблона обработки ошибок, которые я вижу в своем коде.В реальном мире, конечно, есть намного больше.

Код работает, но выглядит слишком сложным, и я не уверен, ударит ли мне специальное значение «ноль» по лицув какой-то момент.

Я также попробовал комбинации .Where() и .Merge(), но код быстро становится нечитаемым.

Есть ли более чистый (то есть более канонический) способ выполнить эту базовуюобработка ошибок?

Ответы [ 2 ]

0 голосов
/ 26 сентября 2018

Это немного помогает:

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)));
    }
}
0 голосов
/ 24 сентября 2018

Для этого уже есть отличные библиотеки, например 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 с перехватом SqlException или ExecuteHttpCallRetryAsync для обработки HttpRequestException и обработки их другим способом.

...