Ich komme zu spät zur Party, aber hier ist meine Lernreise zu diesem kniffligen Thema.
1. Wo finden wir den offiziellen Anwalt für die Wiederverwendung von HttpClient?
Ich meine, wenn die Wiederverwendung von HttpClient beabsichtigt ist
und dies wichtig ist , ist ein solcher Befürworter besser in seiner eigenen API-Dokumentation dokumentiert, als in vielen "Advanced Topics", "Performance (anti) pattern" oder anderen Blog-Posts versteckt zu sein . Ansonsten, wie soll ein neuer Lernender es wissen, bevor es zu spät ist?
Ab sofort (Mai 2018) verweist das erste Suchergebnis beim Googeln von "c # httpclient" auf diese API-Referenzseite auf MSDN , die diese Absicht überhaupt nicht erwähnt. Nun, Lektion 1 hier für Anfänger ist, klicken Sie immer auf den Link "Andere Versionen" direkt nach der Überschrift der MSDN-Hilfeseite. Dort finden Sie wahrscheinlich Links zur "aktuellen Version". In diesem HttpClient-Fall werden Sie zum neuesten Dokument
mit dieser Absichtsbeschreibung weitergeleitet .
Ich vermute, dass viele Entwickler, die neu in diesem Thema waren, auch nicht die richtige Dokumentationsseite gefunden haben. Deshalb ist dieses Wissen nicht weit verbreitet, und die Leute waren überrascht, als sie es später herausfanden
, möglicherweise auf schwierige Weise .
2. Die (falsche?) Vorstellung von using
IDisposable
Dies ist ein wenig vom Thema abweichend, aber es ist immer noch erwähnenswert, dass es kein Zufall ist, zu sehen, dass die Leute in diesen oben genannten Blog-Posts beschuldigen, dass HttpClient
die IDisposable
Benutzeroberfläche dazu neigt, das using (var client = new HttpClient()) {...}
Muster zu verwenden , und dann zum Problem führen.
Ich glaube, dass dies auf eine unausgesprochene (falsche?) Vorstellung zurückzuführen ist:
"Es wird erwartet, dass ein IDisposable-Objekt von kurzer Dauer ist" .
JEDOCH, obwohl es sicherlich kurzlebig ist, wenn wir Code in diesem Stil schreiben:
using (var foo = new SomeDisposableObject())
{
...
}
Die offizielle Dokumentation zu IDisposable
erwähnt niemals, dass IDisposable
Objekte von kurzer Dauer sein müssen. IDisposable ist per Definition lediglich ein Mechanismus, mit dem Sie nicht verwaltete Ressourcen freigeben können. Nichts mehr. In diesem Sinne wird ERWARTET, dass Sie eventuell die Entsorgung auslösen, dies ist jedoch nicht von kurzer Dauer.
Es ist daher Ihre Aufgabe, den richtigen Zeitpunkt für die Entsorgung zu bestimmen, basierend auf den Anforderungen des Lebenszyklus Ihres realen Objekts. Nichts hindert Sie daran, ein IDisposable dauerhaft zu verwenden:
using System;
namespace HelloWorld
{
class Hello
{
static void Main()
{
Console.WriteLine("Hello World!");
using (var client = new HttpClient())
{
for (...) { ... } // A really long loop
// Or you may even somehow start a daemon here
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
Mit diesem neuen Verständnis können wir jetzt, wenn wir diesen Blog-Beitrag erneut besuchen , deutlich feststellen, dass der "Fix" HttpClient
einmal initialisiert wird, ihn aber nie wieder freigibt. Aus diesem Grund können wir anhand seiner Netstat-Ausgabe erkennen, dass die Verbindung im Zustand ESTABLISHED verbleibt, was bedeutet, dass er besteht NICHT richtig geschlossen worden. Wenn es geschlossen wäre, wäre sein Status stattdessen 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 kann nach dem Fix noch einen Leistungsgewinn verzeichnen. Dennoch ist es konzeptionell falsch, IDisposable die Schuld zu geben und sich dafür zu entscheiden, NICHT darüber zu verfügen.
3. Müssen wir HttpClient in eine statische Eigenschaft oder sogar als Singleton setzen?
Basierend auf dem Verständnis des vorherigen Abschnitts wird die Antwort hier meines Erachtens klar: "nicht unbedingt". Es hängt wirklich davon ab, wie Sie Ihren Code organisieren, solange Sie einen HTTP-Client wiederverwenden UND (im Idealfall) irgendwann entsorgen.
Komischerweise
stimmt das Beispiel im
Abschnitt "Bemerkungen" des aktuellen offiziellen Dokuments nicht ganz. Es definiert eine "GoodController" -Klasse, die eine statische HttpClient-Eigenschaft enthält, die nicht entsorgt wird. was nicht gehorcht, was ein anderes Beispiel im Abschnitt
" Beispiele" betont: "muss entsorgen ... damit die App keine Ressourcen verliert ".
Und schließlich ist Singleton nicht ohne eigene Herausforderungen.
"Wie viele Leute halten globale Variablen für eine gute Idee? Niemand.
Wie viele Leute halten Singleton für eine gute Idee? Ein paar.
Was gibt? Singletons sind nur ein paar globale Variablen. "
- Zitiert aus diesem inspirierenden Vortrag "Global State and Singletons"
PS: SqlConnection
Dies ist für die aktuelle Frage und Antwort irrelevant, aber es ist wahrscheinlich gut zu wissen. Das Verwendungsmuster von SqlConnection ist unterschiedlich. Sie müssen SqlConnection NICHT erneut verwenden , da es den Verbindungspool auf diese Weise besser handhabt .
Der Unterschied ergibt sich aus dem Implementierungsansatz. Jede HttpClient-Instanz verwendet ihren eigenen Verbindungspool (von hier aus zitiert
). aber SqlConnection selbst wird von einem zentralen Verbindungspool verwaltet werden , nach diesem .
Und Sie müssen SqlConnection weiterhin wie für HttpClient entsorgen.