Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Создание надежных HTTP-приложений, которые могут восстановиться после временных ошибок сбоя, является общим требованием. В этой статье предполагается, что вы уже прочитали общие сведения о устойчивой разработке приложений, так как в этой статье представлены основные понятия. Чтобы помочь в создании устойчивых HTTP-приложений, пакет NuGet Microsoft.Extensions.Http.Resilience предоставляет механизмы устойчивости специально для этого HttpClient. Этот пакет NuGet использует библиотеку Microsoft.Extensions.Resilience
и Polly, который является популярным проектом с открытым исходным кодом. Дополнительные сведения см. в разделе Polly.
Начало работы
Чтобы использовать шаблоны устойчивости в HTTP-приложениях, установите пакет NuGet Microsoft.Extensions.Http.Resilience .
dotnet add package Microsoft.Extensions.Http.Resilience --version 8.0.0
Дополнительные сведения см. в разделе dotnet package add или Управление зависимостями пакетов в приложениях .NET.
Добавление устойчивости к HTTP-клиенту
Чтобы добавить устойчивость к HttpClient объекту, необходимо вызвать метод IHttpClientBuilder, который возвращается при вызове любого из доступных AddHttpClient методов. Дополнительные сведения см. статью IHttpClientFactory с .NET.
Доступно несколько расширений, ориентированных на устойчивость. Некоторые из них являются стандартными, поэтому используют различные отраслевые рекомендации, а другие — более настраиваемыми. При добавлении устойчивости следует добавить только один обработчик устойчивости и избегать наложения обработчиков. Если необходимо добавить несколько обработчиков устойчивости, следует рассмотреть возможность использования AddResilienceHandler
метода расширения, что позволяет настроить стратегии устойчивости.
Внимание
Все примеры в этой статье зависят от API AddHttpClient из библиотеки Microsoft.Extensions.Http, которая возвращает экземпляр IHttpClientBuilder. Экземпляр IHttpClientBuilder используется для настройки HttpClient и добавления обработчика устойчивости.
Добавьте стандартный обработчик устойчивости
Стандартный обработчик устойчивости использует несколько сложенных друг на друга стратегий устойчивости, с параметрами по умолчанию для отправки запросов и обработки временных ошибок. Стандартный обработчик устойчивости добавляется через вызов AddStandardResilienceHandler
метода расширения на экземпляре IHttpClientBuilder.
var services = new ServiceCollection();
var httpClientBuilder = services.AddHttpClient<ExampleClient>(
configureClient: static client =>
{
client.BaseAddress = new("https://jsonplaceholder.typicode.com");
});
Предыдущий код:
- Создает экземпляр ServiceCollection.
- Добавляет элемент HttpClient для типа
ExampleClient
в контейнер службы. - Настраивает HttpClient для использования
"https://jsonplaceholder.typicode.com"
в качестве базового адреса. - Создает этот объект
httpClientBuilder
, используемый в других примерах в этой статье.
Более реальный пример будет зависеть от размещения, такого как описано в статье .NET Generic Host. Используя пакет NuGet Microsoft.Extensions.Hosting, рассмотрим следующий обновленный пример:
using Http.Resilience.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
IHttpClientBuilder httpClientBuilder = builder.Services.AddHttpClient<ExampleClient>(
configureClient: static client =>
{
client.BaseAddress = new("https://jsonplaceholder.typicode.com");
});
Предыдущий код аналогичен ручному подходу к созданию ServiceCollection
, но вместо этого используется Host.CreateApplicationBuilder() для построения хоста, который предоставляет службы.
Определяется ExampleClient
следующим образом:
using System.Net.Http.Json;
namespace Http.Resilience.Example;
/// <summary>
/// An example client service, that relies on the <see cref="HttpClient"/> instance.
/// </summary>
/// <param name="client">The given <see cref="HttpClient"/> instance.</param>
internal sealed class ExampleClient(HttpClient client)
{
/// <summary>
/// Returns an <see cref="IAsyncEnumerable{T}"/> of <see cref="Comment"/>s.
/// </summary>
public IAsyncEnumerable<Comment?> GetCommentsAsync()
{
return client.GetFromJsonAsAsyncEnumerable<Comment>("/comments");
}
}
Предыдущий код:
- Определяет
ExampleClient
тип, имеющий конструктор, который принимает объект HttpClient. - Предоставляет метод
GetCommentsAsync
, который отправляет GET запрос в конечную точку/comments
и возвращает ответ.
Тип Comment
определяется следующим образом:
namespace Http.Resilience.Example;
public record class Comment(
int PostId, int Id, string Name, string Email, string Body);
Учитывая, что вы создали IHttpClientBuilder (httpClientBuilder
) и теперь понимаете реализацию ExampleClient
и соответствующую Comment
модель, рассмотрим следующий пример:
httpClientBuilder.AddStandardResilienceHandler();
В приведенный выше код добавляется стандартный обработчик устойчивости HttpClient. Как и большинство API устойчивости, предусмотрены перегрузки, позволяющие настраивать параметры по умолчанию и стратегии устойчивости.
Удалите стандартные обработчики устойчивости
Существует метод RemoveAllResilienceHandlers, который удаляет все ранее зарегистрированные обработчики устойчивости. Это полезно, если необходимо очистить существующие обработчики отказоустойчивости и добавить ваши собственные.
В следующем примере показано, как сконфигурировать пользовательский HttpClient, используя метод AddHttpClient
, удалить все заранее определённые стратегии устойчивости и заменить их новыми обработчиками.
Этот подход позволяет очистить существующие конфигурации и определить новые в соответствии с вашими требованиями.
// By default, we want all HttpClient instances to include the StandardResilienceHandler.
services.ConfigureHttpClientDefaults(builder => builder.AddStandardResilienceHandler());
// For a named HttpClient "custom" we want to remove the StandardResilienceHandler and add the StandardHedgingHandler instead.
services.AddHttpClient("custom")
.RemoveAllResilienceHandlers()
.AddStandardHedgingHandler();
Предыдущий код:
- Создает экземпляр ServiceCollection.
- Добавляет стандартный обработчик устойчивости ко всем экземплярам HttpClient.
- Для HttpClient"custom" :
- Удаляет все предопределенные обработчики устойчивости, которые были зарегистрированы ранее. Это полезно, если вы хотите начать с чистого листа, чтобы добавить собственные пользовательские стратегии.
- Добавляет
StandardHedgingHandler
в HttpClient. Вы можете заменитьAddStandardHedgingHandler()
любой стратегией, которая соответствует потребностям приложения, таким как механизмы повторных попыток, выключатели цепи или другие методы устойчивости.
Стандартные обработчики отказоустойчивости по умолчанию
Конфигурация по умолчанию объединяет пять стратегий устойчивости в следующем порядке (от самого внешнего к самому внутреннему):
заказ | Стратегия | Описание | Параметры по умолчанию |
---|---|---|---|
1 | Ограничение скорости | Механизм ограничения запросов ограничивает максимальное количество одновременных запросов, отправляемых зависимому компоненту. | Очередь: 0 Разрешение: 1_000 |
2 | Общий тайм-аут | Общая система ограничения времени ожидания запроса применяет общее время ожидания к процессу выполнения, гарантируя, что запрос, включая повторные попытки, не превышает настроенное ограничение. | Общее время ожидания: 30s |
3 | Повторить попытку | Конвейер повторных попыток повторяет запрос в случае замедления зависимости или возвращает временную ошибку. | Максимальное число повторных попыток: 3 Откат: Exponential Используйте джиттер: true Задержка:2s |
4 | Средство разбиения цепи | Ломатель цепи блокирует выполнение, если обнаружено слишком много непосредственных отказов или таймаутов. | Соотношение сбоев: 10 % Минимальная пропускная способность: 100 Длительность выборки: 30s Длительность перерыва: 5s |
5 | Тайм-аут попытки | Конвейер с ограничением времени ожидания ограничивает длительность каждой попытки запроса и выдаёт исключение, если оно превышено. | Тайм-аут ожидания попытки: 10 сек. |
Повторные попытки и прерыватели цепи
Стратегии повторных попыток и останова цепи обрабатывают набор определенных кодов состояния HTTP и исключений. Рассмотрим следующие коды состояния HTTP:
- HTTP 500 и более поздних версий (ошибки сервера)
- HTTP 408 (истекло время ожидания запроса)
- HTTP 429 (слишком много запросов)
Кроме того, эти стратегии обрабатывают следующие исключения:
HttpRequestException
TimeoutRejectedException
Отключение повторных попыток для заданного списка методов HTTP
По умолчанию стандартный обработчик устойчивости настроен для повторных попыток для всех методов HTTP. Для некоторых приложений такое поведение может быть нежелательным или даже вредным. Например, если запрос POST вставляет новую запись в базу данных, то повторные попытки для такого запроса могут привести к дублированию данных. Если необходимо отключить повторные попытки для заданного списка методов HTTP, можно использовать метод DisableFor(HttpRetryStrategyOptions, HttpMethod[]):
httpClientBuilder.AddStandardResilienceHandler(options =>
{
options.Retry.DisableFor(HttpMethod.Post, HttpMethod.Delete);
});
Кроме того, можно использовать метод DisableForUnsafeHttpMethods(HttpRetryStrategyOptions), который отключает повторные попытки для запросов POST
, PATCH
, PUT
, DELETE
и CONNECT
запросов. Согласно RFC, эти методы считаются небезопасными; означает, что их семантика не доступна только для чтения:
httpClientBuilder.AddStandardResilienceHandler(options =>
{
options.Retry.DisableForUnsafeHttpMethods();
});
Добавить стандартный обработчик хеджирования
Стандартный обработчик хеджирования оборачивает выполнение запроса стандартным механизмом хеджирования. Параллельное хеджирование замедляет повторные попытки выполнения запросов.
Чтобы использовать стандартный обработчик хэджинга, вызовите AddStandardHedgingHandler
метод расширения. В следующем примере ExampleClient
настраивается для использования стандартного обработчика хеджирования.
httpClientBuilder.AddStandardHedgingHandler();
В предыдущий код добавляется стандартный обработчик хеджирования в HttpClient.
Стандартные обработчики хеджирования по умолчанию
Стандартное хеджирование использует пул автоматических выключателей, чтобы гарантировать, что неработающие конечные точки не хеджируются. По умолчанию выбор из пула основан на URL (схема + хост + порт).
Подсказка
Рекомендуется настроить способ выбора стратегий путем вызова StandardHedgingHandlerBuilderExtensions.SelectPipelineByAuthority
или StandardHedgingHandlerBuilderExtensions.SelectPipelineBy
для более сложных сценариев.
В предыдущий код добавляется стандартный обработчик хеджирования в IHttpClientBuilder. Конфигурация по умолчанию объединяет пять стратегий устойчивости в следующем порядке (от самого внешнего к самому внутреннему):
заказ | Стратегия | Описание | Параметры по умолчанию |
---|---|---|---|
1 | Общее время ожидания запроса | Общая система ограничения времени ожидания запроса применяет общий тайм-аут на выполнение задачи, гарантируя, что запрос, включая попытки хеджирования, не превышает установленное ограничение. | Общее время ожидания: 30s |
2 | Хеджирование | Стратегия хеджирования выполняет запросы к нескольким конечным точкам в случае, если зависимость медленно работает или возвращает временную ошибку. Маршрутизация — это параметры, по умолчанию он просто хеджирует URL-адрес, предоставленный исходным HttpRequestMessage. | Минимальное количество попыток: 1 Максимальное количество попыток: 10 Задержка: 2 с |
3 | Ограничение скорости (на конечную точку) | Механизм ограничения запросов ограничивает максимальное количество одновременных запросов, отправляемых зависимому компоненту. | Очередь: 0 Разрешение: 1_000 |
4 | Разделитель цепи (на каждом конце) | Ломатель цепи блокирует выполнение, если обнаружено слишком много непосредственных отказов или таймаутов. | Соотношение сбоев: 10 % Минимальная пропускная способность: 100 Длительность выборки: 30s Длительность перерыва: 5s |
5 | Время ожидания истечения попытки (для каждой конечной точки) | Конвейер с ограничением времени ожидания ограничивает длительность каждой попытки запроса и выдаёт исключение, если оно превышено. | Время ожидания: 10s |
Настройка выбора маршрута обработчика хеджирования
При использовании стандартного обработчика хеджирования можно настроить способ выбора конечных точек запроса путем вызова различных расширений в типе IRoutingStrategyBuilder
. Это может быть полезно для таких сценариев, как тестирование A/B, где требуется направлять процент запросов в другую конечную точку:
httpClientBuilder.AddStandardHedgingHandler(static (IRoutingStrategyBuilder builder) =>
{
// Hedging allows sending multiple concurrent requests
builder.ConfigureOrderedGroups(static options =>
{
options.Groups.Add(new UriEndpointGroup()
{
Endpoints =
{
// Imagine a scenario where 3% of the requests are
// sent to the experimental endpoint.
new() { Uri = new("https://example.net/api/experimental"), Weight = 3 },
new() { Uri = new("https://example.net/api/stable"), Weight = 97 }
}
});
});
});
Предыдущий код:
- Добавляет обработчик хеджирования в IHttpClientBuilder.
- Настраивает
IRoutingStrategyBuilder
для использования методаConfigureOrderedGroups
при настройке упорядоченных групп. - Добавляет в
EndpointGroup
orderedGroup
правило маршрутизации, которое направляет 3% запросов к конечной точкеhttps://example.net/api/experimental
и 97% запросов к конечной точкеhttps://example.net/api/stable
. - Настраивает
IRoutingStrategyBuilder
, чтобы использовать методConfigureWeightedGroups
для настройки.
Чтобы настроить весовую группу, вызовите метод ConfigureWeightedGroups
на типе IRoutingStrategyBuilder
. Следующий пример настраивает IRoutingStrategyBuilder
на использование метода ConfigureWeightedGroups
для настройки взвешенных групп.
httpClientBuilder.AddStandardHedgingHandler(static (IRoutingStrategyBuilder builder) =>
{
// Hedging allows sending multiple concurrent requests
builder.ConfigureWeightedGroups(static options =>
{
options.SelectionMode = WeightedGroupSelectionMode.EveryAttempt;
options.Groups.Add(new WeightedUriEndpointGroup()
{
Endpoints =
{
// Imagine A/B testing
new() { Uri = new("https://example.net/api/a"), Weight = 33 },
new() { Uri = new("https://example.net/api/b"), Weight = 33 },
new() { Uri = new("https://example.net/api/c"), Weight = 33 }
}
});
});
});
Предыдущий код:
- Добавляет обработчик хеджирования в IHttpClientBuilder.
- Настраивает
IRoutingStrategyBuilder
для использования методаConfigureWeightedGroups
в настройке взвешенных групп. - Устанавливает
SelectionMode
наWeightedGroupSelectionMode.EveryAttempt
. - Добавляет
WeightedEndpointGroup
вweightedGroup
, который распределяет 33% запросов на конечную точкуhttps://example.net/api/a
, 33% запросов на конечную точкуhttps://example.net/api/b
и 33% запросов на конечную точкуhttps://example.net/api/c
.
Подсказка
Максимальное количество попыток хеджирования напрямую сопоставляется с числом настроенных групп. Например, если у вас есть две группы, максимальное количество попыток составляет два.
Дополнительные сведения см. в документации Polly: стратегия повышения устойчивости.
Обычно можно настроить упорядоченную группу или взвешенную группу, но возможно настроить обе. Использование упорядоченных и взвешированных групп полезно в сценариях, когда вы хотите отправить процент запросов в другую конечную точку, например, при тестировании A/B.
Добавьте пользовательские обработчики устойчивости
Чтобы получить дополнительные возможности управления, можно настроить обработчики устойчивости с помощью AddResilienceHandler
API. Этот метод принимает делегата, который конфигурирует экземпляр ResiliencePipelineBuilder<HttpResponseMessage>
, используемый для создания стратегий повышения устойчивости.
Чтобы настроить именованный обработчик устойчивости, вызовите AddResilienceHandler
метод расширения с именем обработчика. В следующем примере настраивается именованный обработчик стабильности "CustomPipeline"
.
httpClientBuilder.AddResilienceHandler(
"CustomPipeline",
static builder =>
{
// See: https://www.pollydocs.org/strategies/retry.html
builder.AddRetry(new HttpRetryStrategyOptions
{
// Customize and configure the retry logic.
BackoffType = DelayBackoffType.Exponential,
MaxRetryAttempts = 5,
UseJitter = true
});
// See: https://www.pollydocs.org/strategies/circuit-breaker.html
builder.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions
{
// Customize and configure the circuit breaker logic.
SamplingDuration = TimeSpan.FromSeconds(10),
FailureRatio = 0.2,
MinimumThroughput = 3,
ShouldHandle = static args =>
{
return ValueTask.FromResult(args is
{
Outcome.Result.StatusCode:
HttpStatusCode.RequestTimeout or
HttpStatusCode.TooManyRequests
});
}
});
// See: https://www.pollydocs.org/strategies/timeout.html
builder.AddTimeout(TimeSpan.FromSeconds(5));
});
Предыдущий код:
- Добавляет обработчик резилиентности с именем
"CustomPipeline"
в качествеpipelineName
в контейнер службы. - Добавляет стратегию повторных попыток с экспоненциальной задержкой повторов, пятью попытками и предпочитаемым интервалом случайных изменений времени в механизм повышения надежности.
- Добавляет стратегию размыкания цепи с периодом выборки 10 секунд, коэффициентом отказов 0,2 (20%), минимальным количеством операций в секунду, равным трем, а также предикат, обрабатывающий
RequestTimeout
иTooManyRequests
коды состояния HTTP, к системе обеспечения устойчивости. - Добавляет стратегию тайм-аута с истечением 5 секунд в конструктор устойчивости.
Существует множество вариантов для каждой стратегии устойчивости. Дополнительные сведения см. в документации Полли : Стратегии. Для получения дополнительной информации о настройке ShouldHandle
делегатов см. документацию Polly: Обработка ошибок в реактивных стратегиях.
Предупреждение
Если вы используете стратегии повторных попыток и времени ожидания, и вы хотите настроить ShouldHandle
делегат в стратегии повторных попыток, убедитесь, следует ли обрабатывать исключение времени ожидания Polly. Полли бросает TimeoutRejectedException
(который наследует от Exception), вместо стандартного TimeoutException.
Динамическая перезагрузка
Polly поддерживает динамическую перезагрузку настроенных стратегий устойчивости. Это означает, что можно изменить конфигурацию стратегий устойчивости во время выполнения. Чтобы включить динамическую перезагрузку, используйте соответствующую AddResilienceHandler
перегрузку, которая открывает ResilienceHandlerContext
. Учитывая контекст, вызовите EnableReloads
соответствующих опций стратегии устойчивости.
httpClientBuilder.AddResilienceHandler(
"AdvancedPipeline",
static (ResiliencePipelineBuilder<HttpResponseMessage> builder,
ResilienceHandlerContext context) =>
{
// Enable reloads whenever the named options change
context.EnableReloads<HttpRetryStrategyOptions>("RetryOptions");
// Retrieve the named options
var retryOptions =
context.GetOptions<HttpRetryStrategyOptions>("RetryOptions");
// Add retries using the resolved options
builder.AddRetry(retryOptions);
});
Предыдущий код:
- Добавляет обработчик резилиентности с именем
"AdvancedPipeline"
в качествеpipelineName
в контейнер службы. - Обеспечивает перезагрузку конвейера
"AdvancedPipeline"
при изменении именованныхRetryStrategyOptions
опций. - Извлекает именованные параметры из службы IOptionsMonitor<TOptions>.
- Добавляет стратегию повторных попыток с полученными параметрами в модуль устойчивости.
Для получения дополнительных сведений см. документацию Полли: расширенное внедрение зависимостей.
В этом примере используется раздел параметров, который может изменяться, например файл appsettings.json . Рассмотрите следующий файл appsettings.json:
{
"RetryOptions": {
"Retry": {
"BackoffType": "Linear",
"UseJitter": false,
"MaxRetryAttempts": 7
}
}
}
Теперь представьте, что эти параметры привязаны к конфигурации приложения, привязав его HttpRetryStrategyOptions
к разделу "RetryOptions"
:
var section = builder.Configuration.GetSection("RetryOptions");
builder.Services.Configure<HttpStandardResilienceOptions>(section);
Для получения дополнительной информации см. шаблон параметров в .NET.
Пример использования
Ваше приложение использует внедрение зависимостей для разрешения ExampleClient
и соответствующего HttpClient. Код создает IServiceProvider и извлекает из него ExampleClient
.
IHost host = builder.Build();
ExampleClient client = host.Services.GetRequiredService<ExampleClient>();
await foreach (Comment? comment in client.GetCommentsAsync())
{
Console.WriteLine(comment);
}
Предыдущий код:
- Создает IServiceProvider из ServiceCollection.
- Разрешает
ExampleClient
из IServiceProvider. - Вызывает метод
GetCommentsAsync
наExampleClient
, чтобы получить комментарии. - Записывает каждый комментарий в консоль.
Представьте себе ситуацию, когда сеть исчезнет или сервер не отвечает. На следующей схеме показано, как стратегии устойчивости будут обрабатывать ситуацию, учитывая ExampleClient
и метод GetCommentsAsync
:
На приведенной выше схеме показано следующее:
- Запрос
ExampleClient
HTTP GET отправляется в конечную точку/comments
. - Вычисляется HttpResponseMessage :
- Если ответ выполнен успешно (HTTP 200), возвращается ответ.
- Если ответ неуспешен (HTTP не 200), система устойчивости использует настроенные стратегии надежности.
Хотя это простой пример, он демонстрирует, как стратегии устойчивости можно использовать для обработки временных ошибок. Дополнительные сведения смотрите в документации Полли: Стратегии.
Известные проблемы
В следующих разделах подробно описаны различные известные проблемы.
Совместимость с пакетом Grpc.Net.ClientFactory
Если вы используете Grpc.Net.ClientFactory
версию 2.63.0
или более раннюю, то включение стандартных обработчиков устойчивости или резервирования для клиента gRPC может вызвать исключение во время выполнения. В частности, рассмотрим следующий пример кода:
services
.AddGrpcClient<Greeter.GreeterClient>()
.AddStandardResilienceHandler();
Предыдущий код приводит к следующему исключению:
System.InvalidOperationException: The ConfigureHttpClient method is not supported when creating gRPC clients. Unable to create client with name 'GreeterClient'.
Чтобы устранить эту проблему, рекомендуется обновить до Grpc.Net.ClientFactory
версии 2.64.0
или более поздней.
Существует проверка во время сборки, которая проверяет, используете ли вы версию Grpc.Net.ClientFactory
или более раннюю 2.63.0
, и если да, создаётся предупреждение о компиляции. Предупреждение можно отключить, задав в файле проекта следующее свойство:
<PropertyGroup>
<SuppressCheckGrpcNetClientFactoryVersion>true</SuppressCheckGrpcNetClientFactoryVersion>
</PropertyGroup>
Совместимость с .NET Application Insights
Если вы используете .NET Application Insights версии 2.22.0 или более поздней, то включение функций устойчивости в приложении может привести к тому, что все данные телеметрии Application Insights отсутствуют. Проблема возникает при регистрации функций устойчивости перед службами Application Insights. Рассмотрим следующий пример, вызывающий проблему:
// At first, we register resilience functionality.
services.AddHttpClient().AddStandardResilienceHandler();
// And then we register Application Insights. As a result, Application Insights doesn't work.
services.AddApplicationInsightsTelemetry();
Эту проблему можно устранить, обновив .NET Application Insights до версии 2.23.0 или более поздней. Если вы не можете выполнить обновление, зарегистрируйте службы Application Insights перед функциями устойчивости, как показано ниже. Это исправит проблему.
// We register Application Insights first, and now it will be working correctly.
services.AddApplicationInsightsTelemetry();
services.AddHttpClient().AddStandardResilienceHandler();