Каковы накладные расходы на создание нового HttpClient на один вызов в клиенте WebAPI?

Каким должно быть время жизни 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); } } 

HttpClient был разработан для повторного использования для нескольких вызовов . Даже через несколько streamов. HttpClientHandler имеет учетные данные и cookies, которые предназначены для повторного использования во всех вызовах. Наличие нового экземпляра HttpClient требует повторной настройки всего этого материала. Кроме того, свойство DefaultRequestHeaders содержит свойства, предназначенные для нескольких вызовов. Необходимость сброса этих значений по каждому запросу поражает точку.

Другим важным преимуществом HttpClient является возможность добавления HttpMessageHandlers в HttpMessageHandlers запроса / ответа для применения проблем с перекрестными разрезами. Это может быть регистрация, аудит, дросселирование, переадресация, автономная обработка, захват показателей. Всевозможные вещи. Если для каждого запроса создается новый HttpClient, тогда все эти обработчики сообщений должны быть настроены для каждого запроса, и каким-то образом должно быть предусмотрено состояние любого уровня приложения, которое совместно используется между запросами этих обработчиков.

Чем больше вы используете функции HttpClient , тем больше вы увидите, что повторное использование существующего экземпляра имеет смысл.

Тем не менее, самая большая проблема, на мой взгляд, заключается в том, что когда class HttpClient размещен, он предоставляет HttpClientHandler , который затем принудительно закрывает соединение TCP/IP в пуле соединений, управляемых ServicePointManager . Это означает, что каждый запрос с новым HttpClient требует повторного установления нового соединения TCP/IP .

Из моих тестов, используя простой HTTP в локальной сети, поражение производительности довольно незначительно. Я подозреваю, что это связано с тем, что существует основной протокол TCP keepalive, который поддерживает соединение, даже когда HttpClientHandler пытается его закрыть.

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

Я подозреваю, что удар по HTTPS соединению будет еще хуже.

Мой совет – сохранить экземпляр HttpClient для срока службы вашего приложения для каждого отдельного API, к которому вы подключаетесь.

Если вы хотите, чтобы ваше приложение масштабировалось, разница была ОГРОМНОЙ! В зависимости от нагрузки вы увидите очень разные показатели производительности. Как упоминает Даррел Миллер, HttpClient был разработан для повторного использования в разных запросах. Это подтвердили ребята из команды BCL, которые ее написали.

Недавний проект, который я имел, заключался в том, чтобы помочь очень крупному и известному онлайн-магазину компьютеров в масштабе всей сети для Black Friday / holiday traffic для некоторых новых систем. Мы столкнулись с некоторыми проблемами производительности, связанными с использованием 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 предназначен для создания экземпляра один раз и повторного использования на протяжении всего срока службы приложения. Особенно в серверных приложениях создание нового экземпляра HttpClient для каждого запроса приведет к исчерпанию количества сокетов, доступных при тяжелых нагрузках. Это приведет к ошибкам SocketException.

Если вы обнаружите, что вам нужно использовать статический HttpClient с разными заголовками, базовым адресом и т. Д., Что вам нужно будет сделать, так это создать HttpRequestMessage вручную и установить эти значения в HttpRequestMessage . Затем используйте HttpClient:SendAsync(HttpRequestMessage requestMessage, ...)

Как утверждают другие ответы, HttpClient предназначен для повторного использования. Однако повторное использование одного экземпляра HttpClient в многопоточном приложении означает, что вы не можете изменять значения его свойств с сохранением состояния, например BaseAddress и DefaultRequestHeaders (так что вы можете использовать их только в том случае, если они постоянны в вашем приложении).

Одним из способов обойти это ограничение является перенос HttpClient с classом, который дублирует все HttpClient методы GetAsync ( GetAsync , PostAsync т. Д.) И делегирует их одному HttpClient . Однако это довольно утомительно (вам также нужно будет обернуть методы расширения ), и, к счастью, существует другой способ – продолжать создавать новые экземпляры HttpClient , но повторно использовать базовый HttpClientHandler . Просто убедитесь, что вы не распоряжаетесь обработчиком:

 HttpClientHandler _sharedHandler = new HttpClientHandler(); //never dispose this HttpClient GetClient(string token) { //client code can dispose these HttpClient instances return new HttpClient(_sharedHandler, disposeHandler: false) { DefaultRequestHeaders = { Authorization = new AuthenticationHeaderValue("Bearer", token) } }; } 

Связано с большими объемами веб-сайтов, но не напрямую с HttpClient. У нас есть fragment кода ниже во всех наших сервисах.

 // number of milliseconds after which an active System.Net.ServicePoint connection is closed. const int DefaultConnectionLeaseTimeout = 60000; ServicePoint sp = ServicePointManager.FindServicePoint(new Uri("http://")); sp.ConnectionLeaseTimeout = DefaultConnectionLeaseTimeout; 

От 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) & й = истина

«Вы можете использовать это свойство, чтобы гарантировать, что активные соединения объекта ServicePoint не остаются открытыми неограниченно. Это свойство предназначено для сценариев, в которых соединения должны периодически удаляться и восстанавливаться, например сценарии балансировки нагрузки.

По умолчанию, когда KeepAlive является истинным для запроса, свойство MaxIdleTime устанавливает тайм-аут для закрытия соединений ServicePoint из-за неактивности. Если ServicePoint имеет активные соединения, MaxIdleTime не действует, и соединения остаются открытыми на неопределенный срок.

Если для свойства ConnectionLeaseTimeout установлено значение, отличное от -1, и по истечении указанного времени активное соединение ServicePoint закрывается после обслуживания запроса, устанавливая KeepAlive равным false в этом запросе. Установка этого значения влияет на все подключения, управляемые объектом ServicePoint. ”

Когда у вас есть службы за CDN или другой конечной точкой, которые вы хотите переустановить, этот параметр помогает абонентам следовать за вами в ваш новый пункт назначения. В этом примере через 60 секунд после перехода на другой ресурс все вызывающие абоненты должны повторно подключиться к новой конечной точке. Это требует, чтобы вы знали свои зависимые сервисы (те услуги, которые вы звоните) и их конечные точки.

Вы также можете обратиться к этому сообщению в блоге Сайmonoм Тимсом: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Но HttpClient отличается. Хотя он реализует интерфейс IDisposable это фактически общий объект. Это означает, что под крышками он является реентерабельным) и streamобезопасным. Вместо создания нового экземпляра HttpClient для каждого выполнения вы должны поделиться одним экземпляром HttpClient на протяжении всего срока службы приложения. Давайте посмотрим, почему.

  • Установка HttpContext.Current.Session в модульном тесте
  • Как я могу динамически переключать адреса веб-сервисов в .NET без перекомпиляции?
  • Как вручную установить веб-сервис на Tomcat 6?
  • Выбор SSL-сертификата клиента в Java
  • Почему статические методы не используются в качестве веб-служб в веб-службах ASMX?
  • Каковы различия между веб-службами WCF и ASMX?
  • Большой запрос веб-службы WCF с ошибкой (400) Ошибка HTTP-запроса
  • Исключение JAXB: class, не известный этому контексту
  • Как передать массив JSON в качестве параметра в URL-адресе
  • JQuery Ajax Проводка json для webservice
  • Java: функция переопределения для отключения проверки сертификата SSL
  • Давайте будем гением компьютера.