Рекомендации по использованию HttpClient
Класс System.Net.Http.HttpClient отправляет HTTP-запросы и получает http-ответы от ресурса, определяемого URI. Экземпляр HttpClient представляет собой коллекцию параметров, применяемых ко всем запросам, выполняемым этим экземпляром, и каждый экземпляр использует собственный пул подключений, который изолирует свои запросы от других. Начиная с .NET Core 2.1, SocketsHttpHandler класс предоставляет реализацию, обеспечивая согласованность поведения на всех платформах.
Поведение DNS
HttpClient разрешает записи DNS только при создании подключения. Он не отслеживает срок жизни (TTL), указанный DNS-сервером. Если записи DNS регулярно изменяются, что может произойти в некоторых сценариях, клиент не будет уважать эти обновления. Чтобы устранить эту проблему, можно ограничить время существования подключения, задав PooledConnectionLifetime свойство, чтобы поиск DNS повторялся при замене подключения. Рассмотрим следующий пример:
var handler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(15) // Recreate every 15 minutes
};
var sharedClient = new HttpClient(handler);
Предыдущий HttpClient
параметр настраивается для повторного использования подключений в течение 15 минут. После того как истечет интервал времени, указанный PooledConnectionLifetime, и соединение завершит свой последний связанный запрос (если таковой имеется), это соединение закрывается. Если в очереди ожидают какие-либо запросы, при необходимости создается новое подключение.
Интервал в 15 минут был выбран произвольно для иллюстрации. Вы должны выбрать значение на основе ожидаемой частоты ИЗМЕНЕНИЙ DNS или других сетевых изменений.
Подключения в пуле
Пул соединений для объекта HttpClient связан с базовым SocketsHttpHandler. Когда экземпляр HttpClient удаляется, все существующие подключения в пуле также удаляются. Если вы позже отправите запрос на тот же сервер, необходимо повторно создать новое подключение. В результате происходит снижение производительности из-за создания ненужных подключений. Кроме того, TCP-порты не выпускаются сразу после закрытия подключения. (Дополнительные сведения об этом см. в статье TCP TIME-WAIT
в RFC 9293.) Если скорость запросов высока, ограничение операционной системы доступных портов может быть исчерпано. Чтобы избежать проблем с исчерпанием портов, рекомендуется повторно использовать HttpClient экземпляры для максимально возможного количества HTTP-запросов.
Рекомендуемое использование
Чтобы суммировать рекомендуемое HttpClient
использование с точки зрения управления временем существования, следует использовать либо долгоживущие клиенты и настроить PooledConnectionLifetime
(.NET Core и .NET 5+) или краткоживущие клиенты, созданные IHttpClientFactory
.
В .NET Core и .NET 5+:
- Используйте
static
одноэлементныйHttpClient экземпляр с PooledConnectionLifetime установленным на нужный интервал, например 2 минуты, в зависимости от ожидаемых изменений DNS. Это решает проблемы как с нехваткой портов, так и с изменениями DNS, без добавления дополнительных затрат IHttpClientFactory. Если вам нужно иметь возможность имитировать обработчик, его можно зарегистрировать отдельно.
Совет
Если вы используете только ограниченное количество HttpClient экземпляров, это также допустимая стратегия. Самое важное это то, что они не создаются и не удаляются при каждом запросе, так как они содержат пул подключений. Использование нескольких экземпляров необходимо для сценариев с несколькими прокси-серверами или для разделения контейнеров cookie без полной отключения обработки файлов cookie.
С помощью IHttpClientFactoryможно использовать несколько разных клиентов, настроенных для разных вариантов использования. Тем не менее, помните, что созданные фабрикой клиенты предназначены для кратковременного существования, и после создания клиента фабрика больше не имеет контроля над ним.
Фабрика создает пул экземпляров HttpMessageHandler, и, если срок существования обработчика не истек, его можно повторно использовать из пула при создании нового экземпляра HttpClient. Это повторное использование позволяет избежать проблем с исчерпанием сокета.
Если вас интересует предоставляемая IHttpClientFactory настройка, мы рекомендуем использовать подход с типизированным клиентом.
- Используйте
В платформе .NET Framework используйте IHttpClientFactory для управления экземплярами
HttpClient
. Если вы не используете фабрику и вместо этого создаете новый экземпляр клиента для каждого запроса самостоятельно, вы можете исчерпать ресурсы доступных портов.Совет
Если вашему приложению требуются файлы cookie, подумайте об отключении автоматической обработки этих файлов или об избегании IHttpClientFactory. Объединение экземпляров HttpMessageHandler приводит к совместному использованию объектов CookieContainer. Непредвиденное совместное использование объектов CookieContainer часто приводит к ошибкам в коде.
Дополнительные сведения об управлении временем существования HttpClient
с помощью IHttpClientFactory
см. в рекомендациях IHttpClientFactory
.
Устойчивость со статическими клиентами
Можно настроить static
или синглтон клиент для использования любого количества потоков устойчивости с помощью следующего шаблона:
using Microsoft.Extensions.Http.Resilience;
using Polly;
class MyClass
{
static HttpClient? s_httpClient;
MyClass()
{
var retryPipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddRetry(new HttpRetryStrategyOptions
{
BackoffType = DelayBackoffType.Exponential,
MaxRetryAttempts = 3
})
.Build();
var socketHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(15)
};
var resilienceHandler = new ResilienceHandler(retryPipeline)
{
InnerHandler = socketHandler,
};
s_httpClient = new HttpClient(resilienceHandler);
}
}
Предыдущий код:
- Использует пакет NuGet Microsoft.Extensions.Http.Resilience.
- Указывает временный обработчик ошибок HTTP, настроенный с конвейером повторных попыток, при которых интервалы задержки будут экспоненциально увеличиваться с каждой попыткой.
- Устанавливает время существования подключения в пуле на пятнадцать минут для
socketHandler
. - Передается
socketHandler
вresilienceHandler
с логикой повторных попыток. - Создает общий экземпляр
HttpClient
, учитываяresilienceHandler
.