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

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

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

Набрасывая это, я получаю что-то вроде

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(), но код быстро становится нечитаемым.

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
327
2

Ответы 2

Для этого уже есть отличные библиотеки, например Полли. Но, если вы хотите реализовать свое собственное, вы можете сделать что-то вроде следующего.

Вы можете создать класс 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 и обработки их другим способом.

Спасибо за ссылку на Полли, выглядит полезно! Но даже имея библиотеку, нужно было бы реализовать это в Rx. Меня больше интересовало, как это сделать конкретно в Rx, а не хорошая реализация для этого.

Krumelur 24.09.2018 15:49

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

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

Другие вопросы по теме