Каковы накладные расходы на создание нового HttpClient для каждого вызова в клиенте WebAPI?
что должно быть HttpClient
срок службы клиентской части веб-API?
Лучше ли иметь один экземпляр 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}tt{2}", product.Name, product.Price, product.Category);
}
}
5 ответов:
HttpClient
была предназначен для повторного использования для нескольких вызовов. Даже через несколько потоков. ЭлементHttpClientHandler
учетные данные и файлы, которые предназначены для повторного использования во время вызовов. Имея новыйHttpClient
экземпляр требует переустановки всех этих вещей. Кроме того,DefaultRequestHeaders
свойство содержит свойства, предназначенные для нескольких вызовов. Необходимость сбрасывать эти значения при каждом запросе побеждает точку.еще одно важное преимущество
HttpClient
- Это возможность добавитьHttpMessageHandlers
в конвейер запроса / ответа для применения сквозных проблем. Это может быть ведение журнала, аудит, регулирование, перенаправление обработки, автономная обработка, захват метрик. Всякие разные вещи. Если новый HttpClient создается для каждого запроса, то все эти обработчики сообщений должны быть настроены для каждого запроса и каким-то образом должно быть предоставлено любое состояние уровня приложения, которое совместно используется между запросами для этих обработчиков.более вы используете функции
HttpClient
, тем больше вы увидите, что повторное использование существующего экземпляра имеет смысл.Однако, самая большая проблема, на мой взгляд, что когда
HttpClient
класс утилизируется, он утилизируетHttpClientHandler
, который затем принудительно закрываетTCP/IP
соединение в пуле соединений, которым управляетServicePointManager
. Это означает, что каждый запрос с новойHttpClient
требуется восстановление новогоTCP/IP
подключение.из моих тестов, используя простой HTTP на a Лан, хит производительности довольно незначителен. Я подозреваю, что это связано с тем, что существует базовый TCP keepalive, который удерживает соединение открытым, даже когда
HttpClientHandler
пытается закрыть его.на запросы, которые идут через интернет, я видел другую историю. Я видел 40% производительности из-за необходимости повторного открытого запроса каждый раз.
Я подозреваю, что хит на
HTTPS
связь будет еще хуже.мой совет -сохранить экземпляр HttpClient для жизни вашего приложения для каждого отдельного API, к которому вы подключаетесь.
если вы хотите, чтобы ваше приложение масштабировалось, разница огромна! В зависимости от нагрузки, вы увидите очень разные показатели производительности. Как Даррел Миллер упоминает, 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 предназначен для однократного создания экземпляра и повторного использования в течение всего срока службы приложения. Особенно в серверных приложениях, создание нового экземпляра HttpClient для каждый запрос вымотает количество гнезд доступных под тяжелыми нагрузками. Это приведет к ошибкам SocketException.
если вы обнаружите, что вам нужно использовать статические
HttpClient
с различными заголовками, базовым адресом и т. д. то, что вам нужно сделать, это создатьHttpRequestMessage
вручную и установите эти значения наHttpRequestMessage
. Затем используйтеHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
как говорится в других ответах,
HttpClient
предназначена для повторного использования. Однако, повторное использование одногоHttpClient
экземпляр в многопоточном приложении означает, что вы не можете изменить значения его свойств с сохранением состояния, напримерBaseAddress
иDefaultRequestHeaders
(таким образом, вы можете использовать их только в том случае, если они постоянны в вашем приложении).один из способов обойти это ограничение является упаковка
HttpClient
С классом, который дублирует всеHttpClient
методы (GetAsync
,PostAsync
etc) и делегирует их в синглтон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. У нас есть фрагмент кода ниже во всех наших услуг.
// number of milliseconds after which an active System.Net.ServicePoint connection is closed. const int DefaultConnectionLeaseTimeout = 60000; ServicePoint sp = ServicePointManager.FindServicePoint(new Uri("http://<yourServiceUrlHere>")); sp.ConnectionLeaseTimeout = DefaultConnectionLeaseTimeout;
" это свойство можно использовать, чтобы убедиться, что активные подключения объекта ServicePoint не выполняются оставайтесь открытыми до бесконечности. Это свойство предназначено для сценариев, в которых соединения должны периодически удаляться и восстанавливаться, например для сценариев балансировки нагрузки.
по умолчанию, когда KeepAlive имеет значение true для запроса, свойство MaxIdleTime задает время ожидания для закрытия соединений ServicePoint из-за бездействия. Если точка обслуживания имеет активные соединения, MaxIdleTime не имеет никакого эффекта, и соединения остаются открытыми бесконечно.
когда ConnectionLeaseTimeout свойству присваивается значение, отличное от -1, и по истечении указанного времени соединение active ServicePoint закрывается после обслуживания запроса, задав для параметра KeepAlive значение false в этом запросе. Установка этого значения влияет на все соединения, управляемые объектом ServicePoint."
если у вас есть службы за CDN или другой конечной точкой, которую вы хотите отрабатывать отказоустойчивость, то этот параметр помогает вызывающим абонентам следовать за вами к новому месту назначения. В этом примере через 60 секунд после отработки отказа все абоненты необходимо повторно подключиться к новой конечной точке. Это требует, чтобы вы знали свои зависимые службы (те службы, которые вы вызываете) и их конечные точки.
вы также можете обратиться к этому сообщению в блоге Саймона Тиммса:https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
но
HttpClient
разное. Хотя он реализуетIDisposable
интерфейс это на самом деле общий объект. Это означает, что под покровами он реентерабелен) и потокобезопасен. Вместо создания нового экземпляраHttpClient
для каждого выполнения вы должны использовать один экземплярHttpClient
на всю продолжительность жизни приложение. Давайте посмотрим, почему.