Каким должен быть HttpClient
срок службы клиента WebAPI?
Лучше иметь один экземпляр HttpClient
для нескольких звонков?
Каковы затраты на создание и размещение HttpClient
каждого запроса, как в примере ниже (взято с http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from- a-net-client ):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// New code:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
}
c#
asp.net
web-services
asp.net-web-api
dotnet-httpclient
Бруно Пессанья
источник
источник
Stopwatch
класс для его оценки. Моя оценка была бы более разумнойHttpClient
, если бы все эти экземпляры использовались в одном и том же контексте.Ответы:
HttpClient
был разработан для многократного использования . Даже через несколько потоков.HttpClientHandler
Имеет полномочия и Куки, которые предназначены , чтобы быть повторно использованы между вызовами. Наличие новогоHttpClient
экземпляра требует переустановки всего этого. ТакжеDefaultRequestHeaders
свойство содержит свойства, которые предназначены для нескольких вызовов. Необходимость сбрасывать эти значения в каждом запросе побеждает точку.Еще одним важным преимуществом
HttpClient
является возможность добавленияHttpMessageHandlers
в конвейер запросов / ответов для применения сквозных задач. Это могут быть записи, аудит, регулирование, обработка перенаправления, автономная обработка, получение метрик. Все виды разных вещей. Если новый HttpClient создается при каждом запросе, то все эти обработчики сообщений должны быть настроены для каждого запроса, и каким-либо образом должно быть предоставлено любое состояние уровня приложения, которое совместно используется запросами для этих обработчиков.Чем больше вы используете функции
HttpClient
, тем больше вы увидите, что повторное использование существующего экземпляра имеет смысл.Однако самая большая проблема, на мой взгляд, заключается в том, что когда
HttpClient
класс удаляется, он удаляетсяHttpClientHandler
, что затем принудительно закрываетTCP/IP
соединение в пуле соединений, которым управляетServicePointManager
. Это означает, что каждый запрос с новымHttpClient
требует восстановления новогоTCP/IP
соединения.Из моих тестов, использующих простой HTTP в локальной сети, снижение производительности довольно незначительно. Я подозреваю, что это потому, что есть основной протокол активности TCP, который удерживает соединение открытым, даже когда
HttpClientHandler
пытается его закрыть.По запросам, которые идут через интернет, я видел другую историю. Я видел снижение производительности на 40% из-за необходимости каждый раз заново открывать запрос.
Я подозреваю, что попадание в
HTTPS
соединение будет еще хуже.Мой совет - сохранить экземпляр HttpClient на время жизни вашего приложения для каждого отдельного API, к которому вы подключаетесь.
источник
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager
Насколько вы уверены в этом утверждении? В это трудно поверить.HttpClient
выглядит для меня как единица работы, которая должна часто создаваться.Если вы хотите, чтобы ваше приложение масштабировалось, разница огромна! В зависимости от нагрузки вы увидите очень разные показатели производительности. Как упоминает Даррел Миллер, HttpClient был разработан для повторного использования в запросах. Это подтвердили ребята из команды BCL, которые написали это.
Недавний проект, который у меня был, состоял в том, чтобы помочь очень крупному и известному интернет-продавцу компьютеров в масштабировании для Черной пятницы / праздничного трафика для некоторых новых систем. Мы столкнулись с некоторыми проблемами производительности, связанными с использованием HttpClient. Поскольку он реализуется
IDisposable
, разработчики сделали то, что вы обычно делаете, создав экземпляр и поместив его внутриusing()
оператора. Как только мы начали нагрузочное тестирование, приложение поставило сервер на колени - да, сервер не только приложение. Причина в том, что каждый экземпляр HttpClient открывает порт на сервере. Из-за недетерминированного завершения GC и того факта, что вы работаете с компьютерными ресурсами, которые охватывают несколько уровней OSI , закрытие сетевых портов может занять некоторое время. На самом деле сама ОС WindowsМожет потребоваться до 20 секунд, чтобы закрыть порт (для Microsoft). Мы открывали порты быстрее, чем они могли быть закрыты - истощение портов сервера, которое загружало процессор до 100%. Мое исправление состояло в том, чтобы изменить HttpClient на статический экземпляр, который решил проблему. Да, это одноразовый ресурс, но любые накладные расходы значительно перевешиваются разницей в производительности. Я рекомендую вам провести нагрузочное тестирование, чтобы увидеть, как ваше приложение ведет себя.Вы также можете проверить страницу руководства WebAPI с документацией и примером на https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Обратите особое внимание на этот призыв:
Если вы обнаружите, что вам нужно использовать статический элемент
HttpClient
с разными заголовками, базовым адресом и т. Д., Вам нужно будет создатьHttpRequestMessage
вручную и установить эти значения вHttpRequestMessage
. Затем используйтеHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
ОБНОВЛЕНИЕ для .NET Core .
IHttpClientFactory
Для созданияHttpClient
экземпляров следует использовать внедрение через зависимости . Он будет управлять временем жизни для вас, и вам не нужно явно распоряжаться им. См. Создание HTTP-запросов с использованием IHttpClientFactory в ASP.NET Core.источник
Как говорится в других ответах,
HttpClient
предназначен для повторного использования. Однако повторное использование одногоHttpClient
экземпляра в многопоточном приложении означает, что вы не можете изменять значения его свойств с состоянием, таких какBaseAddress
иDefaultRequestHeaders
(поэтому вы можете использовать их, только если они постоянны в вашем приложении).Один из способов обойти это ограничение - обернуть
HttpClient
классом, который дублирует всеHttpClient
необходимые вам методы (GetAsync
иPostAsync
т. Д.) И делегирует их одноэлементномуHttpClient
. Однако это довольно утомительно (вам также нужно обернуть методы расширения ), и, к счастью, есть и другой способ - продолжать создавать новыеHttpClient
экземпляры, но повторно использовать базовыеHttpClientHandler
. Просто убедитесь, что вы не избавляетесь от обработчика:источник
SendAsync
гораздо менее удобно, чем специальные методы, такие какPutAsync
иPostAsJsonAsync
т. д.Связано с большими объемами веб-сайтов, но не напрямую с HttpClient. У нас есть фрагмент кода ниже во всех наших службах.
Из https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2); к (DevLang-CSharp) & й = TRUE
«Это свойство можно использовать, чтобы активные соединения объекта ServicePoint не оставались открытыми бесконечно. Это свойство предназначено для сценариев, в которых следует периодически прерывать и восстанавливать подключения, например, для сценариев балансировки нагрузки.
По умолчанию, когда KeepAlive имеет значение true для запроса, свойство MaxIdleTime устанавливает время ожидания для закрытия соединений ServicePoint из-за неактивности. Если у ServicePoint есть активные соединения, MaxIdleTime не имеет никакого эффекта, и соединения остаются открытыми бесконечно.
Если для свойства ConnectionLeaseTimeout установлено значение, отличное от -1, и по истечении указанного времени активное соединение ServicePoint закрывается после обслуживания запроса, установив для KeepAlive значение false в этом запросе. Установка этого значения влияет на все соединения, управляемые объектом ServicePoint. "
Если у вас есть службы за CDN или другой конечной точкой, которые вы хотите переключать при сбое, тогда этот параметр помогает вызывающим абонентам следовать за вами к вашему новому месту назначения. В этом примере через 60 секунд после аварийного переключения все абоненты должны повторно подключиться к новой конечной точке. Это требует, чтобы вы знали ваши зависимые сервисы (те сервисы, которые вы вызываете) и их конечные точки.
источник
Вы также можете обратиться к этому сообщению в блоге Саймона Тиммса: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
источник
Стоит отметить, что ни одно из примечаний блога «не использовать, используя», это то, что вам нужно учитывать не только BaseAddress и DefaultHeader. Как только вы сделаете HttpClient статическим, существуют внутренние состояния, которые будут передаваться через запросы. Пример: вы проходите проверку подлинности для третьей стороны с помощью HttpClient для получения токена FedAuth (игнорируйте, почему не используете OAuth / OWIN / и т. Д.), В этом ответном сообщении есть заголовок Set-Cookie для FedAuth, он добавляется в ваше состояние HttpClient. Следующий пользователь, который войдет в ваш API, будет отправлять последний файл cookie FedAuth, если вы не управляете этими файлами cookie при каждом запросе.
источник
В качестве первой проблемы, хотя этот класс является одноразовым, использование его с
using
оператором не лучший выбор, потому что даже когда вы удаляетеHttpClient
объект, базовый сокет не освобождается сразу и может вызвать серьезную проблему с именем «исчерпание сокетов».Но есть вторая проблема, с
HttpClient
которой вы можете столкнуться, когда используете его как одноэлементный или статический объект. В этом случае синглтон или статикаHttpClient
не учитываютDNS
изменения.в ядре .net вы можете сделать то же самое с HttpClientFactory примерно так:
ConfigureServices
документация и пример здесь
источник