Was soll das sein HttpClient
Lebensdauer eines WebAPI-Clients sein?
Ist es besser, eine Instanz von HttpClient
für mehrere Anrufe zu haben?
Was ist der Aufwand für das Erstellen und Entsorgen einer HttpClient
Anfrage, wie im folgenden Beispiel (entnommen aus 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
Bruno Pessanha
quelle
quelle
Stopwatch
Klasse zum Benchmarking verwenden können. Nach meiner Einschätzung ist es sinnvoller, eine einzige zu habenHttpClient
, vorausgesetzt, alle diese Instanzen werden im selben Kontext verwendet.Antworten:
HttpClient
wurde für die Wiederverwendung für mehrere Anrufe entwickelt . Auch über mehrere Threads hinweg. DasHttpClientHandler
verfügt über Anmeldeinformationen und Cookies, die für Anrufe wiederverwendet werden sollen. Um eine neueHttpClient
Instanz zu haben, müssen Sie all diese Dinge neu einrichten. AußerdemDefaultRequestHeaders
enthält die Eigenschaft Eigenschaften, die für mehrere Aufrufe vorgesehen sind. Wenn Sie diese Werte bei jeder Anforderung zurücksetzen müssen, wird der Punkt zunichte gemacht.Ein weiterer großer Vorteil von
HttpClient
ist die Möglichkeit,HttpMessageHandlers
die Anforderungs- / Antwort-Pipeline zu erweitern, um Querschnittsthemen anzuwenden. Diese können zum Protokollieren, Überwachen, Drosseln, Umleiten, Offline-Behandeln und Erfassen von Metriken verwendet werden. Alle möglichen Dinge. Wenn für jede Anforderung ein neuer HttpClient erstellt wird, müssen alle diese Nachrichtenhandler für jede Anforderung eingerichtet werden, und es muss auch jeder Status auf Anwendungsebene bereitgestellt werden, der von Anforderungen für diese Handler gemeinsam genutzt wird.Je mehr Sie die Funktionen von nutzen
HttpClient
, desto mehr werden Sie feststellen, dass die Wiederverwendung einer vorhandenen Instanz sinnvoll ist.Das größte Problem ist jedoch meiner Meinung nach, dass eine
HttpClient
Klasse , wenn sie entsorgt wirdHttpClientHandler
, entsorgt , wodurch dieTCP/IP
Verbindung in dem von ihnen verwalteten Verbindungspool zwangsweise geschlossen wirdServicePointManager
. Dies bedeutet, dass für jede Anforderung mit einer neuenHttpClient
eine neueTCP/IP
Verbindung wiederhergestellt werden muss .Nach meinen Tests mit einfachem HTTP in einem LAN ist der Leistungseinbruch ziemlich vernachlässigbar. Ich vermute, dies liegt daran, dass ein zugrunde liegendes TCP-Keepalive die Verbindung offen hält, selbst wenn
HttpClientHandler
versucht wird, sie zu schließen.Auf Anfragen, die über das Internet gehen, habe ich eine andere Geschichte gesehen. Ich habe einen Leistungseinbruch von 40% festgestellt, weil ich die Anfrage jedes Mal neu öffnen musste.
Ich vermute, dass der Treffer bei einer
HTTPS
Verbindung noch schlimmer wäre.Mein Rat ist , eine Instanz von HttpClient für die Lebensdauer Ihrer Anwendung für jede einzelne API, mit der Sie eine Verbindung herstellen, beizubehalten.
quelle
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager
Wie sicher sind Sie sich über diese Aussage? Das ist kaum zu glauben.HttpClient
sieht für mich aus wie eine Arbeitseinheit, die oft instanziiert werden soll.Wenn Sie möchten, dass Ihre Anwendung skaliert, ist der Unterschied RIESIG! Abhängig von der Last sehen Sie sehr unterschiedliche Leistungszahlen. Wie Darrel Miller erwähnt, wurde der HttpClient so konzipiert, dass er bei allen Anforderungen wiederverwendet werden kann. Dies wurde von den Leuten im BCL-Team bestätigt, die es geschrieben haben.
Ein aktuelles Projekt war es, einem sehr großen und bekannten Online-Computerhändler dabei zu helfen, für einige neue Systeme den Black Friday / Holiday-Verkehr zu skalieren. Bei der Verwendung von HttpClient sind einige Leistungsprobleme aufgetreten. Da es implementiert wird
IDisposable
, haben die Entwickler das getan, was Sie normalerweise tun würden, indem sie eine Instanz erstellt und in eineusing()
Anweisung eingefügt haben . Als wir mit dem Lasttest begannen, brachte die App den Server in die Knie - ja, der Server nicht nur die App. Der Grund ist, dass jede Instanz von HttpClient einen Port auf dem Server öffnet. Aufgrund der nicht deterministischen Finalisierung von GC und der Tatsache, dass Sie mit Computerressourcen arbeiten, die sich über mehrere OSI-Ebenen erstrecken , kann das Schließen von Netzwerkports eine Weile dauern. In der Tat Windows-Betriebssystem selbstDas Schließen eines Ports kann bis zu 20 Sekunden dauern (laut Microsoft). Wir haben Ports schneller geöffnet, als sie geschlossen werden konnten - Erschöpfung des Server-Ports, die die CPU auf 100% hämmerte. Mein Fix bestand darin, den HttpClient in eine statische Instanz zu ändern, die das Problem löste. Ja, es handelt sich um eine verfügbare Ressource, aber der Overhead wird durch den Leistungsunterschied bei weitem aufgewogen. Ich empfehle Ihnen, einige Lasttests durchzuführen, um zu sehen, wie sich Ihre App verhält.Dokumentation und Beispiel finden Sie auch auf der Seite WebAPI-Anleitung unter https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Achten Sie besonders auf diesen Hinweis:
Wenn Sie feststellen, dass Sie eine Statik
HttpClient
mit unterschiedlichen Headern, Basisadressen usw. verwenden müssen, müssen Sie dieHttpRequestMessage
manuell erstellen und diese Werte auf dem festlegenHttpRequestMessage
. Verwenden Sie dann dieHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
UPDATE für .NET Core : Sie sollten die
IHttpClientFactory
Via Dependency Injection verwenden, umHttpClient
Instanzen zu erstellen . Es wird die Lebensdauer für Sie verwalten und Sie müssen es nicht explizit entsorgen. Siehe Erstellen von HTTP-Anforderungen mit IHttpClientFactory in ASP.NET Corequelle
Wie die anderen Antworten besagen,
HttpClient
ist es zur Wiederverwendung gedacht. Wenn Sie jedoch eine einzelneHttpClient
Instanz in einer Multithread-Anwendung wiederverwenden , können Sie die Werte ihrer statusbehafteten Eigenschaften wieBaseAddress
und nicht ändernDefaultRequestHeaders
(Sie können sie also nur verwenden, wenn sie in Ihrer Anwendung konstant sind).Ein Ansatz für das Erhalten diese Einschränkung um ist Verpackung
HttpClient
mit einer Klasse , die alle die DuplikateHttpClient
Methoden , die Sie brauchen (GetAsync
,PostAsync
usw.) und die Delegierten sie zu einem SingletonHttpClient
. Dies ist jedoch ziemlich langwierig (Sie müssen auch die Erweiterungsmethoden umbrechen ), und zum Glück gibt es einen anderen Weg : Erstellen Sie weiterhin neueHttpClient
Instanzen, aber verwenden Sie den zugrunde liegenden Wert erneutHttpClientHandler
. Stellen Sie nur sicher, dass Sie den Handler nicht entsorgen:quelle
SendAsync
ist viel weniger bequem als die speziellen Methoden wiePutAsync
,PostAsJsonAsync
usw.Bezogen auf Websites mit hohem Volumen, jedoch nicht direkt auf HttpClient. Wir haben den folgenden Codeausschnitt in allen unseren Diensten.
Von 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
"Mit dieser Eigenschaft können Sie sicherstellen, dass die aktiven Verbindungen eines ServicePoint-Objekts nicht auf unbestimmte Zeit geöffnet bleiben. Diese Eigenschaft ist für Szenarien vorgesehen, in denen Verbindungen regelmäßig getrennt und wiederhergestellt werden sollten, z. B. Lastausgleichsszenarien.
Wenn KeepAlive für eine Anforderung wahr ist, legt die MaxIdleTime-Eigenschaft standardmäßig das Zeitlimit für das Schließen von ServicePoint-Verbindungen aufgrund von Inaktivität fest. Wenn der ServicePoint über aktive Verbindungen verfügt, hat MaxIdleTime keine Auswirkung und die Verbindungen bleiben auf unbestimmte Zeit geöffnet.
Wenn die ConnectionLeaseTimeout-Eigenschaft auf einen anderen Wert als -1 festgelegt ist und die angegebene Zeit abgelaufen ist, wird eine aktive ServicePoint-Verbindung nach der Bearbeitung einer Anforderung geschlossen, indem KeepAlive in dieser Anforderung auf false gesetzt wird. Das Festlegen dieses Werts wirkt sich auf alle Verbindungen aus, die vom ServicePoint-Objekt verwaltet werden. "
Wenn Sie Dienste hinter einem CDN oder einem anderen Endpunkt haben, für den Sie ein Failover durchführen möchten, hilft diese Einstellung Anrufern, Ihnen zu Ihrem neuen Ziel zu folgen. In diesem Beispiel sollten 60 Sekunden nach einem Failover alle Anrufer erneut eine Verbindung zum neuen Endpunkt herstellen. Es erfordert, dass Sie Ihre abhängigen Dienste (die Dienste, die SIE aufrufen) und deren Endpunkte kennen.
quelle
Vielleicht möchten Sie auch auf diesen Blog-Beitrag von Simon Timms verweisen: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
quelle
Eine Sache, auf die Sie hinweisen sollten, ist, dass keiner der Blog-Hinweise "Nicht verwenden" lautet, dass Sie nicht nur die BaseAddress und den DefaultHeader berücksichtigen müssen. Sobald Sie HttpClient statisch gemacht haben, gibt es interne Zustände, die über Anforderungen hinweg übertragen werden. Ein Beispiel: Sie authentifizieren sich bei einem Drittanbieter mit HttpClient, um ein FedAuth-Token zu erhalten (ignorieren Sie, warum nicht OAuth / OWIN / etc verwendet wird). Diese Antwortnachricht enthält einen Set-Cookie-Header für FedAuth. Dieser wird Ihrem HttpClient-Status hinzugefügt. Der nächste Benutzer, der sich bei Ihrer API anmeldet, sendet das FedAuth-Cookie der letzten Person, es sei denn, Sie verwalten diese Cookies bei jeder Anforderung.
quelle
Als erstes Problem ist die Verwendung dieser Klasse mit der
using
Anweisung nicht die beste Wahl, auch wenn Sie darüber verfügenHttpClient
der zugrunde liegende Socket selbst dann nicht sofort freigegeben wird und ein schwerwiegendes Problem mit dem Namen "Sockets-Erschöpfung" verursachen kann.Es gibt jedoch ein zweites Problem
HttpClient
, das auftreten kann, wenn Sie es als Singleton- oder statisches Objekt verwenden. In diesem FallHttpClient
respektiert ein Singleton oder Static nichtDNS
Änderungen nicht.In .net Core können Sie dasselbe mit HttpClientFactory wie folgt tun :
ConfigureServices
Dokumentation und Beispiel hier
quelle