Jaki powinien być HttpClient
czas życia klienta WebAPI?
Czy lepiej jest mieć jedną instancję HttpClient
dla wielu wywołań?
Jakie są narzuty związane z tworzeniem i usuwaniem HttpClient
żądania na żądanie, jak w przykładzie poniżej (pobrane z http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from- klient sieciowy ):
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
Bruno Pessanha
źródło
źródło
Stopwatch
klasy do porównania jej. Moim zdaniem bardziej sensowne byłoby posiadanie jednegoHttpClient
, zakładając, że wszystkie te instancje są używane w tym samym kontekście.Odpowiedzi:
HttpClient
został zaprojektowany do ponownego wykorzystania w wielu połączeniach . Nawet w wielu wątkach.HttpClientHandler
Ma mandatów oraz plików cookie, które są przeznaczone do ponownego użycia w poprzek połączeń. Posiadanie nowejHttpClient
instancji wymaga ponownego skonfigurowania wszystkich tych rzeczy. PonadtoDefaultRequestHeaders
właściwość zawiera właściwości przeznaczone dla wielu wywołań. Konieczność resetowania tych wartości przy każdym żądaniu mija się z celem.Inną ważną zaletą
HttpClient
jest możliwość dodawaniaHttpMessageHandlers
do potoku żądań / odpowiedzi w celu zastosowania przekrojowych problemów. Mogą to być rejestrowanie, audyt, ograniczanie przepustowości, obsługa przekierowań, obsługa offline, przechwytywanie metryk. Wiele różnych rzeczy. Jeśli nowy HttpClient jest tworzony dla każdego żądania, wszystkie te programy obsługi komunikatów muszą być skonfigurowane dla każdego żądania i w jakiś sposób należy również podać stan poziomu aplikacji, który jest współużytkowany między żądaniami dla tych programów obsługi.Im częściej używasz funkcji programu
HttpClient
, tym bardziej przekonasz się, że ponowne wykorzystanie istniejącej instancji ma sens.Jednak moim zdaniem największym problemem jest to, że kiedy
HttpClient
klasa jest usuwana, usuwaHttpClientHandler
, co następnie wymusza zamknięcieTCP/IP
połączenia w puli połączeń, którą zarządzaServicePointManager
. Oznacza to, że każde żądanie z nowymHttpClient
wymaga ponownego ustanowienia nowegoTCP/IP
połączenia.Z moich testów, używając zwykłego protokołu HTTP w sieci LAN, wpływ wydajności jest dość znikomy. Podejrzewam, że dzieje się tak dlatego, że istnieje podstawowa funkcja utrzymywania aktywności TCP, która wstrzymuje połączenie, nawet gdy
HttpClientHandler
próbuje je zamknąć.W przypadku żądań przesyłanych przez Internet spotkałem się z inną historią. Widziałem 40% spadek wydajności z powodu konieczności ponownego otwierania żądania za każdym razem.
Podejrzewam, że uderzenie w
HTTPS
połączenie byłoby jeszcze gorsze.Moja rada jest taka, aby zachować wystąpienie HttpClient przez cały okres istnienia aplikacji dla każdego odrębnego interfejsu API, z którym się łączysz.
źródło
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager
Jak bardzo jesteś pewien tego stwierdzenia? Trudno w to uwierzyć.HttpClient
wygląda dla mnie jak jednostka pracy, która powinna być często tworzona.Jeśli chcesz, aby Twoja aplikacja była skalowalna, różnica jest OGROMNA! W zależności od obciążenia zobaczysz bardzo różne wartości wydajności. Jak wspomina Darrel Miller, HttpClient został zaprojektowany do ponownego wykorzystania w żądaniach. Zostało to potwierdzone przez facetów z zespołu BCL, którzy to napisali.
Niedawnym projektem, który miałem, była pomoc bardzo dużemu i znanemu sklepowi internetowemu z komputerami w zakresie ruchu w Czarny piątek / święta dla niektórych nowych systemów. Wystąpiły problemy z wydajnością związane z użyciem HttpClient. Ponieważ implementuje
IDisposable
, programiści zrobili to, co normalnie zrobiłbyś, tworząc instancję i umieszczając ją wusing()
instrukcji. Kiedy zaczęliśmy testować obciążenie, aplikacja rzuciła serwer na kolana - tak, serwer nie tylko aplikacja. Powodem jest to, że każde wystąpienie HttpClient otwiera port na serwerze. Ze względu na niedeterministyczną finalizację GC i fakt, że pracujesz z zasobami komputerowymi obejmującymi wiele warstw OSI , zamykanie portów sieciowych może trochę potrwać. W rzeczywistości system operacyjny Windows samZamknięcie portu może zająć do 20 sekund (na Microsoft). Otwieraliśmy porty szybciej, niż można było je zamknąć - wyczerpanie portu serwera, które obciążyło procesor w 100%. Moja poprawka polegała na zmianie HttpClient na instancję statyczną, co rozwiązało problem. Tak, jest to zasób jednorazowy, ale różnica w wydajności znacznie przewyższa wszelkie koszty ogólne. Zachęcam do przeprowadzenia testów obciążenia, aby zobaczyć, jak zachowuje się Twoja aplikacja.Możesz również sprawdzić stronę z wytycznymi dotyczącymi interfejsu WebAPI w celu uzyskania dokumentacji i przykładów pod adresem https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Zwróć szczególną uwagę na to wezwanie:
Jeśli okaże się, że musisz użyć statycznej
HttpClient
z różnymi nagłówkami, adresem bazowym itp., Co musisz zrobić, to utworzyćHttpRequestMessage
ręcznie i ustawić te wartości w plikuHttpRequestMessage
. Następnie użyjHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
UPDATE for .NET Core :
IHttpClientFactory
do tworzeniaHttpClient
wystąpień należy użyć metody via Dependency Injection . Będzie zarządzał Twoim życiem i nie musisz go jawnie pozbywać. Zobacz Tworzenie żądań HTTP przy użyciu IHttpClientFactory w ASP.NET Coreźródło
Jak podano w innych odpowiedziach,
HttpClient
jest przeznaczony do ponownego wykorzystania. Jednak ponowne użycie pojedynczegoHttpClient
wystąpienia w aplikacji wielowątkowej oznacza, że nie można zmienić wartości jego właściwości stanowych, takich jakBaseAddress
iDefaultRequestHeaders
(więc można ich używać tylko wtedy, gdy są stałe w całej aplikacji).Jednym ze sposobów na poruszanie się po to ograniczenie jest opakowanie
HttpClient
z klasy, która powiela wszystkieHttpClient
metody, czego potrzeba (GetAsync
,PostAsync
etc) oraz delegatów je do SingletonHttpClient
. Jest to jednak dość żmudne (będziesz musiał również opakować metody rozszerzające ) i na szczęście jest inny sposób - twórz noweHttpClient
instancje, ale ponownie używaj bazowegoHttpClientHandler
. Tylko upewnij się, że nie pozbędziesz się obsługi:źródło
SendAsync
jest znacznie mniej wygodne niż dedykowanych metod, takich jakPutAsync
,PostAsJsonAsync
itd.Powiązane z witrynami sieci Web o dużej objętości, ale nie bezpośrednio z HttpClient. Poniżej mamy fragment kodu we wszystkich naszych usługach.
Od 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); k (DevLang-csharp) & rd = true
„Możesz użyć tej właściwości, aby upewnić się, że aktywne połączenia obiektu ServicePoint nie pozostaną otwarte w nieskończoność. Ta właściwość jest przeznaczona dla scenariuszy, w których połączenia powinny być przerywane i wznawiane okresowo, takich jak scenariusze równoważenia obciążenia.
Domyślnie, gdy KeepAlive ma wartość true dla żądania, właściwość MaxIdleTime ustawia limit czasu dla zamykania połączeń ServicePoint z powodu braku aktywności. Jeśli ServicePoint ma aktywne połączenia, MaxIdleTime nie działa, a połączenia pozostają otwarte przez czas nieokreślony.
Gdy właściwość ConnectionLeaseTimeout jest ustawiona na wartość inną niż-1 i po upływie określonego czasu aktywne połączenie ServicePoint jest zamykane po obsłużeniu żądania przez ustawienie KeepAlive na false w tym żądaniu. Ustawienie tej wartości ma wpływ na wszystkie połączenia zarządzane przez obiekt ServicePoint ”.
Jeśli masz usługi za CDN lub innym punktem końcowym, który chcesz przełączać w tryb failover, to ustawienie pomaga dzwoniącym podążać za Tobą do nowego miejsca docelowego. W tym przykładzie 60 sekund po przełączeniu awaryjnym wszyscy wywołujący powinni ponownie połączyć się z nowym punktem końcowym. Wymaga to znajomości usług zależnych (usług, które TY wywołujesz) i ich punktów końcowych.
źródło
Możesz również odnieść się do tego posta na blogu autorstwa Simona Timmsa: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
źródło
Należy zwrócić uwagę na to, że żadna z notatek z blogów „nie używaj” polega na tym, że należy wziąć pod uwagę nie tylko adres BaseAddress i DefaultHeader. Po ustawieniu HttpClient statycznego istnieją stany wewnętrzne, które będą przenoszone między żądaniami. Przykład: Uwierzytelniasz firmę zewnętrzną za pomocą HttpClient, aby uzyskać token FedAuth (zignoruj, dlaczego nie używasz OAuth / OWIN / itp.), Ta wiadomość odpowiedzi ma nagłówek Set-Cookie dla FedAuth, który jest dodawany do stanu HttpClient. Następny użytkownik, który zaloguje się do Twojego API, wyśle plik cookie FedAuth ostatniej osoby, chyba że zarządzasz tymi plikami cookie przy każdym żądaniu.
źródło
Po pierwsze, podczas gdy ta klasa jest jednorazowa, używanie jej z
using
instrukcją nie jest najlepszym wyborem, ponieważ nawet gdy wyrzucaszHttpClient
obiektu gniazdo bazowe nie jest natychmiast zwalniane i może spowodować poważny problem o nazwie „wyczerpanie gniazd.Ale jest drugi problem
HttpClient
, który możesz mieć, gdy używasz go jako obiektu pojedynczego lub statycznego. W tym przypadku singleton lub staticHttpClient
nie są uwzględnianeDNS
zmian.w .net core możesz zrobić to samo z HttpClientFactory, coś takiego:
ConfigureServices
dokumentację i przykład tutaj
źródło