Отказоустойчивые HTTP-запросы в .NET. Начало
Не всегда всё идет по плану: запросы по сети рандомно завершаются неудачей, серверы приложений перегружаются, и возникают неожиданные ошибки. Отказоустойчивые приложения могут восстанавливаться после временных сбоев и продолжать функционировать. Устойчивость достигается путём проектирования приложений, которые могут корректно обрабатывать сбои и быстро восстанавливаться. Рассмотрим инструменты и методы, которые есть в .NET для создания отказоустойчивых систем.
Зачем?
Отправка HTTP-запросов — распространённый подход к связи между удалёнными сервисами. Но они подвержены сбоям из-за проблем с сетью или сервером. Эти сбои могут нарушить доступность сервисов, особенно по мере увеличения зависимостей и риска каскадных сбоев. Вот несколько стратегий повышения устойчивости:
- Повторные попытки - повторить запрос, который завершился неудачей из-за временной ошибки.
- Тайм-ауты - отмена запросов, которые превышают указанный лимит времени.
- Хэджирование и откаты - альтернативные действия или результаты для неудавшихся операций.
- Аварийное отключение - временное прекращение связи с недоступными сервисами.
Эти стратегии можно использовать по отдельности или в сочетании для оптимальной устойчивости HTTP-запросов.
Конвейеры устойчивости
Начиная с .NET 8 интеграция отказоустойчивости в приложения стала намного проще. Можно использовать Microsoft.Extensions.Resilience и Microsoft.Extensions.Http.Resilience, которые построены на основе Polly. Polly — библиотека отказоустойчивости и обработки временных сбоев в .NET. Она позволяет определять стратегии обеспечения устойчивости, описанные выше. Polly получила новый API в последней версии (V8), который был реализован в сотрудничестве с Microsoft. Если вы ранее использовали Microsoft.Extensions.Http.Polly, теперь рекомендуется использовать следующие пакеты:
Install-Package Microsoft.Extensions.Resilience
Install-Package Microsoft.Extensions.Http.Resilience
Cначала создадим конвейер, состоящий из стратегий отказоустойчивости. Каждая стратегия, которую мы настраиваем как часть конвейера, будет выполняться в порядке конфигурации. Порядок важен! Создадим конвейер с помощью построителя:
ResiliencePipeline pipeline =
new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder()
.Handle<ConflictException>(),
Delay = TimeSpan.FromSeconds(1),
MaxRetryAttempts = 2,
BackoffType = DelayBackoffType.Exponential,
UseJitter = true
})
.AddTimeout(new TimeoutStrategyOptions
{
Timeout = TimeSpan.FromSeconds(10)
})
.Build();
await pipeline.ExecuteAsync(
async ct => await httpClient
.GetAsync("https://google.com", ct),
cancellationToken);
Вот что мы добавляем в конвейер отказоустойчивости:
- AddRetry — настраивает стратегию повторных попыток, которую мы можем дополнительно настроить, передав экземпляр RetryStrategyOptions. Здесь вы предоставляем предикат для свойства ShouldHandle, чтобы определить, какие исключения (ConflictException) должна обрабатывать стратегия. Также мы указываем максимальное количество попыток повтора и настраиваем время ожидания до следующего повтора. В данном случае оно будет экспоненциально расти (DelayBackoffType.Exponential) и в разных случаях будет немного варьироваться (UseJitter). См. подробнее про настройки стратегии повтора.
- AddTimeout — настраивает стратегию тайм-аута, которая выдаст TimeoutRejectedException, если делегат не завершится до истечения времени ожидания. Мы можем передать нужное ожидания в экземпляр TimeoutStrategyOptions. Время ожидания по умолчанию - 30 секунд.
В конце мы строим конвейер, и используем настроенный экземпляр для HTTP-запроса в методе ExecuteAsync.
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/building-resilient-cloud-applications-with-dotnet