System.Net.Http.HttpClient und System.Net.Http.HttpClientHandler in .NET Framework 4.5 implementieren IDisposable (über System.Net.Http.HttpMessageInvoker ).
In der using
Anweisungsdokumentation heißt es:
Wenn Sie ein IDisposable-Objekt verwenden, sollten Sie es in der Regel in einer using-Anweisung deklarieren und instanziieren.
Diese Antwort verwendet dieses Muster:
var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("foo", "bar"),
new KeyValuePair<string, string>("baz", "bazinga"),
});
cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
var result = client.PostAsync("/test", content).Result;
result.EnsureSuccessStatusCode();
}
Die sichtbarsten Beispiele von Microsoft rufen jedoch Dispose()
weder explizit noch implizit auf. Zum Beispiel:
- Der ursprüngliche Blog-Artikel , der die Veröffentlichung von HttpClient ankündigt.
- Die aktuelle MSDN-Dokumentation für HttpClient.
- BingTranslateSample
- GoogleMapsSample
- WorldBankSample
In den Kommentaren der Ankündigung fragte jemand den Microsoft-Mitarbeiter:
Nachdem ich Ihre Beispiele überprüft hatte, stellte ich fest, dass Sie die Dispose-Aktion für die HttpClient-Instanz nicht ausgeführt haben. Ich habe alle Instanzen von HttpClient mit using-Anweisungen in meiner App verwendet und dachte, dass dies der richtige Weg ist, da HttpClient die IDisposable-Schnittstelle implementiert. Bin ich auf dem richtigen Weg?
Seine Antwort war:
Im Allgemeinen ist das richtig, obwohl Sie mit "using" und async vorsichtig sein müssen, da sie sich nicht wirklich in .Net 4 mischen. In .Net 4.5 können Sie "await" in einer "using" -Anweisung verwenden.
Übrigens können Sie denselben HttpClient so oft wiederverwenden, wie Sie möchten, sodass Sie ihn normalerweise nicht immer erstellen / entsorgen.
Der zweite Absatz ist für diese Frage überflüssig. Dabei geht es nicht darum, wie oft Sie eine HttpClient-Instanz verwenden können, sondern darum, ob sie entsorgt werden muss, nachdem Sie sie nicht mehr benötigen.
(Update: Tatsächlich ist dieser zweite Absatz der Schlüssel zur Antwort, wie unten von @DPeden bereitgestellt.)
Meine Fragen sind also:
Ist es angesichts der aktuellen Implementierung (.NET Framework 4.5) erforderlich, Dispose () für HttpClient- und HttpClientHandler-Instanzen aufzurufen? Klarstellung: Mit "notwendig" meine ich, wenn es negative Konsequenzen für die Nichtentsorgung gibt, wie z. B. Ressourcenverlust oder Datenkorruptionsrisiken.
Wenn es nicht notwendig ist, wäre es trotzdem eine "gute Praxis", da sie IDisposable implementieren?
Wenn es notwendig (oder empfohlen) ist, implementiert dieser oben erwähnte Code ihn sicher (für .NET Framework 4.5)?
Wenn für diese Klassen kein Aufruf von Dispose () erforderlich ist, warum wurden sie dann als IDisposable implementiert?
Sind die Microsoft-Beispiele irreführend oder unsicher, wenn sie erforderlich sind oder wenn dies empfohlen wird?
quelle
Flush
nach jedem Schreibvorgang einen aufrufen , und abgesehen von der Unannehmlichkeit, die zugrunde liegenden Ressourcen länger als nötig zu halten, was wird nicht passieren, das für "korrektes Verhalten" erforderlich ist?Antworten:
Der allgemeine Konsens ist, dass Sie HttpClient nicht entsorgen müssen (sollten).
Viele Leute, die eng in die Funktionsweise involviert sind, haben dies erklärt.
Siehe Darrel Millers Blog-Beitrag und einen verwandten SO-Beitrag: Das Crawlen von HttpClient führt zu einem Speicherverlust als Referenz.
Ich würde auch dringend empfehlen, dass Sie das Kapitel HttpClient unter Entwerfen von entwicklungsfähigen Web-APIs mit ASP.NET lesen, um zu erfahren, was unter der Haube vor sich geht, insbesondere den hier zitierten Abschnitt "Lebenszyklus":
Oder öffnen Sie sogar DotPeek.
quelle
Timeout
Immobilie zugewiesen sind, aufeinander stampfen?Die aktuellen Antworten sind etwas verwirrend und irreführend, und es fehlen einige wichtige DNS-Implikationen. Ich werde versuchen zusammenzufassen, wo die Dinge klar stehen.
IDisposable
Objekte idealerweise entsorgt werden, wenn Sie damit fertig sind , insbesondere diejenigen, die Ressourcen für benannte / gemeinsam genutzte Betriebssysteme besitzen . DiesHttpClient
ist keine Ausnahme, da, wie Darrel Miller betont, Stornierungs-Token zugewiesen werden und Anforderungs- / Antwort-Körper nicht verwaltete Streams sein können.Connection:close
nach DNS-Änderungen einen Header sendet . Eine andere Möglichkeit besteht darin, dasHttpClient
auf der Clientseite entweder regelmäßig oder über einen Mechanismus zu recyceln, der Informationen über die DNS-Änderung erhält. Weitere Informationen finden Sie unter https://github.com/dotnet/corefx/issues/11224 (ich empfehle, diese sorgfältig zu lesen, bevor Sie den im verlinkten Blog-Beitrag vorgeschlagenen Code blind verwenden).quelle
Nach meinem Verständnis ist ein Aufruf
Dispose()
nur erforderlich, wenn Ressourcen gesperrt werden, die Sie später benötigen (z. B. eine bestimmte Verbindung). Es wird immer empfohlen , Ressourcen freizugeben, die Sie nicht mehr verwenden, auch wenn Sie sie nicht mehr benötigen, einfach weil Sie im Allgemeinen nicht an Ressourcen festhalten sollten, die Sie nicht verwenden (Wortspiel beabsichtigt).Das Microsoft-Beispiel ist nicht unbedingt falsch. Alle verwendeten Ressourcen werden beim Beenden der Anwendung freigegeben. Und im Fall dieses Beispiels geschieht dies fast unmittelbar nach der
HttpClient
Verwendung des. In ähnlichen Fällen ist ein explizites AufrufenDispose()
etwas überflüssig.Wenn eine Klasse implementiert wird
IDisposable
, ist das Verständnis im Allgemeinen, dass SieDispose()
von ihren Instanzen sprechen sollten, sobald Sie vollständig bereit und in der Lage sind. Ich würde davon ausgehen, dass dies insbesondere in Fällen zutrifft, inHttpClient
denen nicht explizit dokumentiert ist, ob Ressourcen oder Verbindungen gehalten / geöffnet werden. In dem Fall, in dem die Verbindung [bald] wieder verwendet wird, möchten Sie darauf verzichtenDipose()
- in diesem Fall sind Sie nicht "vollständig bereit".Siehe auch: IDisposable.Dispose-Methode und Zeitpunkt des Aufrufs von Dispose
quelle
Dispose()
vorzeitig beenden und müssen einige Sekunden später erneut eine Verbindung herstellen, wenn die vorhandene Verbindung wiederverwendbar ist. Ebenso möchten Sie nicht unnötigDispose()
Bilder oder andere Strukturen erstellen, die Sie möglicherweise in ein oder zwei Minuten neu erstellen müssen.Dispose () ruft den folgenden Code auf, der die von der HttpClient-Instanz geöffneten Verbindungen schließt. Der Code wurde durch Dekompilieren mit dotPeek erstellt.
HttpClientHandler.cs - Entsorgen
Wenn Sie dispose nicht aufrufen, schließt ServicePointManager.MaxServicePointIdleTime, das von einem Timer ausgeführt wird, die http-Verbindungen. Der Standardwert beträgt 100 Sekunden.
ServicePointManager.cs
Wenn Sie die Leerlaufzeit nicht auf unendlich eingestellt haben, ist es sicher, dispose nicht aufzurufen und den Leerlaufverbindungs-Timer einzuschalten und die Verbindungen für Sie zu schließen, obwohl es für Sie besser wäre, dispose in einer using-Anweisung aufzurufen, wenn Sie wissen, dass Sie mit einer HttpClient-Instanz fertig sind und die Ressourcen schneller freigeben.
quelle
Kurze Antwort: Nein, die Aussage in der aktuell akzeptierten Antwort ist NICHT korrekt : "Der allgemeine Konsens ist, dass Sie HttpClient nicht entsorgen müssen (sollten)."
Lange Antwort : BEIDE der folgenden Aussagen sind wahr und gleichzeitig erreichbar:
IDisposable
wird angenommen / empfohlen, ein Objekt zu entsorgen.Und sie stehen nicht unbedingt in Konflikt miteinander. Es geht nur darum, wie Sie Ihren Code organisieren, um ein
HttpClient
UND wiederzuverwenden und es dennoch ordnungsgemäß zu entsorgen.Eine noch längere Antwort aus meiner anderen Antwort :
Es ist kein Zufall, dass Leute in einigen Blog-Posts beschuldigen
HttpClient
,IDisposable
dass sie aufgrund der Benutzeroberfläche dazu neigen, dasusing (var client = new HttpClient()) {...}
Muster zu verwenden , und dann zu einem erschöpften Socket-Handler-Problem führen.Ich glaube, das kommt auf eine unausgesprochene (falsche?) Vorstellung an: "Es wird erwartet, dass ein IDisposable-Objekt nur von kurzer Dauer ist" .
JEDOCH, obwohl es sicherlich wie eine kurzlebige Sache aussieht, wenn wir Code in diesem Stil schreiben:
In der offiziellen Dokumentation zu IDisposable wird niemals erwähnt, dass
IDisposable
Objekte nur von kurzer Dauer sein müssen. Per Definition ist IDisposable lediglich ein Mechanismus, mit dem Sie nicht verwaltete Ressourcen freigeben können. Nichts mehr. In diesem Sinne wird von Ihnen ERWARTET, dass Sie die Entsorgung irgendwann auslösen, dies erfordert jedoch nicht von kurzer Dauer.Es ist daher Ihre Aufgabe, anhand der Lebenszyklusanforderungen Ihres realen Objekts richtig zu entscheiden, wann die Entsorgung ausgelöst werden soll. Nichts hindert Sie daran, ein IDisposable auf langlebige Weise zu verwenden:
Mit diesem neuen Verständnis können wir jetzt, wenn wir diesen Blog-Beitrag erneut besuchen , deutlich feststellen, dass der "Fix"
HttpClient
einmal initialisiert , aber niemals entsorgt wird. Deshalb können wir anhand seiner Netstat-Ausgabe erkennen, dass die Verbindung im Status ESTABLISHED bleibt, was bedeutet, dass sie vorhanden ist NICHT richtig geschlossen worden. Wenn es geschlossen wäre, wäre sein Status stattdessen in TIME_WAIT. In der Praxis ist es keine große Sache, nach dem Ende Ihres gesamten Programms nur eine Verbindung zu verlieren, und das Blog-Poster zeigt nach dem Fix immer noch einen Leistungsgewinn. Dennoch ist es konzeptionell falsch, IDisposable zu beschuldigen und es NICHT zu entsorgen.quelle
HttpClient.Dispose
?HttpClient client
Variablen passen, was eine Programming-101-Sache ist, die Sie vermutlich sowieso schon tun. Möglicherweise können Sie auch noch verwendenusing (...) {...}
. Siehe zum Beispiel das Hello World-Beispiel in meiner Antwort.Da es scheint nicht , dass jemand es noch hier erwähnt hat, auf die neue beste Art und Weise zu verwalten und Httpclient HttpClientHandler in .Net - Core 2.1 ist mit HttpClientFactory .
Es löst die meisten der oben genannten Probleme und Fallstricke auf saubere und benutzerfreundliche Weise. Aus Steve Gordons großartigem Blog-Beitrag :
Fügen Sie Ihrem .Net Core-Projekt (2.1.1 oder höher) die folgenden Pakete hinzu:
Fügen Sie dies zu Startup.cs hinzu:
Injizieren und verwenden:
Entdecken Sie die Reihe von Beiträgen in Steves Blog, um weitere Funktionen zu erhalten.
quelle
In meinem Fall habe ich einen HttpClient in einer Methode erstellt, die den Dienstaufruf tatsächlich ausgeführt hat. Etwas wie:
In einer Azure-Worker-Rolle würde diese Methode nach wiederholtem Aufrufen (ohne den HttpClient zu entsorgen) möglicherweise fehlschlagen
SocketException
(Verbindungsversuch fehlgeschlagen).Ich habe den HttpClient zu einer Instanzvariablen gemacht (auf Klassenebene verfügbar gemacht), und das Problem ist behoben. Also würde ich sagen, ja, entsorgen Sie den HttpClient, vorausgesetzt, er ist sicher (Sie haben keine ausstehenden asynchronen Aufrufe), um dies zu tun.
quelle
In der typischen Verwendung (Antworten <2 GB) ist es nicht erforderlich, die HttpResponseMessages zu entsorgen.
Die Rückgabetypen der HttpClient-Methoden sollten entsorgt werden, wenn ihr Stream-Inhalt nicht vollständig gelesen wird. Andernfalls kann die CLR nicht wissen, dass diese Streams geschlossen werden können, bis sie durch Müll gesammelt wurden.
Wenn Sie die HttpCompletionOption auf ResponseHeadersRead setzen oder die Antwort größer als 2 GB ist, sollten Sie bereinigen. Dies kann durch Aufrufen von Dispose in der HttpResponseMessage oder durch Aufrufen von Dispose / Close in dem aus dem HttpResonseMessage-Inhalt erhaltenen Stream oder durch vollständiges Lesen des Inhalts erfolgen.
Ob Sie Dispose auf dem HttpClient aufrufen, hängt davon ab, ob Sie ausstehende Anforderungen abbrechen möchten oder nicht.
quelle
Wenn Sie HttpClient entsorgen möchten, können Sie dies tun, wenn Sie es als Ressourcenpool einrichten. Und am Ende Ihrer Anwendung verfügen Sie über Ihren Ressourcenpool.
Code:
var handler = HttpClientHander.GetHttpClientHandle (neue Uri ("Basis-URL")).
quelle
Dispose
Methode aufrufen , die Sie bei GC registrieren. Dies sollte oben höher bewertet werden.Die Verwendung der Abhängigkeitsinjektion in Ihrem Konstruktor
HttpClient
erleichtert die Verwaltung der Lebensdauer Ihres Konstruktors. Nehmen Sie das Lebensdauermanagement außerhalb des Codes, der es benötigt, und machen Sie es zu einem späteren Zeitpunkt leicht änderbar.Meine derzeitige Präferenz ist es , eine separate http-Client-Klasse
HttpClient
zu erstellen, die einmal pro Zielendpunktdomäne erbt, und sie dann mithilfe der Abhängigkeitsinjektion zu einem Singleton zu machen.public class ExampleHttpClient : HttpClient { ... }
Dann nehme ich eine Konstruktorabhängigkeit vom benutzerdefinierten http-Client in den Serviceklassen, auf die ich Zugriff auf diese API benötige. Dies löst das Problem der Lebensdauer und hat Vorteile beim Verbindungspooling.
Ein Beispiel finden Sie in der entsprechenden Antwort unter https://stackoverflow.com/a/50238944/3140853
quelle
Bitte lesen Sie meine Antwort auf eine sehr ähnliche Frage weiter unten. Es sollte klar sein, dass Sie behandeln sollten
HttpClient
Instanzen als Singletons behandeln und über Anforderungen hinweg wiederverwenden .Was ist der Aufwand für das Erstellen eines neuen HttpClient pro Aufruf in einem WebAPI-Client?
quelle
Ich denke, man sollte ein Singleton-Muster verwenden, um zu vermeiden, dass Instanzen des HttpClient erstellt und ständig geschlossen werden müssen. Wenn Sie .Net 4.0 verwenden, können Sie einen Beispielcode wie folgt verwenden. Weitere Informationen zum Singleton-Muster finden Sie hier .
Verwenden Sie den folgenden Code.
quelle