Sono in ritardo alla festa, ma ecco il mio viaggio di apprendimento su questo argomento difficile.
1. Dove possiamo trovare l'avvocato ufficiale sul riutilizzo di HttpClient?
Voglio dire, se il riutilizzo di HttpClient è previsto
e farlo è importante , tale avvocato è meglio documentato nella propria documentazione API, piuttosto che essere nascosto in molti "Argomenti avanzati", "Performance (anti) pattern" o altri post di blog là fuori . Altrimenti, come dovrebbe sapere un nuovo studente prima che sia troppo tardi?
A partire da ora (maggio 2018), il primo risultato di ricerca quando si cerca su Google "c # httpclient" punta a questa pagina di riferimento dell'API su MSDN , che non menziona affatto tale intenzione. Bene, la lezione 1 qui per i principianti è, fai sempre clic sul link "Altre versioni" subito dopo il titolo della pagina di aiuto di MSDN, probabilmente troverai i collegamenti alla "versione corrente" lì. In questo caso HttpClient, ti porterà all'ultimo documento
qui contenente quella descrizione dell'intenzione .
Ho il sospetto che molti sviluppatori che erano nuovi a questo argomento non abbiano trovato la pagina di documentazione corretta, ecco perché questa conoscenza non è ampiamente diffusa e le persone sono state sorprese quando l'hanno scoperto in
seguito , forse in modo difficile .
2. La (mis?) Concezione di using
IDisposable
Questo è un po 'fuori tema, ma vale comunque la pena sottolineare che non è una coincidenza vedere le persone nei suddetti post di blog incolpare il modo in cui HttpClient
l' IDisposable
interfaccia fa sì che tendano a utilizzare il using (var client = new HttpClient()) {...}
modello e quindi a causare il problema.
Credo che si tratti di una concezione non detta (mis?):
"Un oggetto IDisposable dovrebbe essere di breve durata" .
TUTTAVIA, mentre certamente sembra una cosa di breve durata quando scriviamo codice in questo stile:
using (var foo = new SomeDisposableObject())
{
...
}
la documentazione ufficiale su IDisposable
non menziona mai IDisposable
oggetti di breve durata. Per definizione, IDisposable è semplicemente un meccanismo che consente di rilasciare risorse non gestite. Niente di più. In tal senso, sei ASPETTATO di innescare lo smaltimento, ma non è necessario che tu lo faccia in un modo di breve durata.
È quindi tuo compito scegliere correttamente quando attivare lo smaltimento, in base ai requisiti del ciclo di vita dell'oggetto reale. Non c'è nulla che ti impedisca di utilizzare un IDisposable in modo longevo:
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();
}
}
}
Con questa nuova comprensione, ora rivisitiamo quel post sul blog , possiamo notare chiaramente che la "correzione" si inizializza HttpClient
una volta ma non la smaltisce mai, ecco perché possiamo vedere dal suo output netstat che, la connessione rimane allo stato STABILITO che significa che ha NON è stato chiuso correttamente. Se fosse chiuso, il suo stato sarebbe invece in TIME_WAIT. In pratica, non è un grosso problema perdere una sola connessione aperta al termine dell'intero programma e il poster del blog vede ancora un miglioramento delle prestazioni dopo la correzione; ma è concettualmente errato incolpare IDisposable e scegliere di NON smaltirlo.
3. Dobbiamo mettere HttpClient in una proprietà statica o persino metterlo come singleton?
Sulla base della comprensione della sezione precedente, penso che la risposta qui sia chiara: "non necessariamente". Dipende davvero da come organizzi il tuo codice, purché riutilizzi un HttpClient e (idealmente) lo elimini alla fine.
Esilarante, nemmeno l'esempio nella
sezione Osservazioni del presente documento ufficiale
fa perfettamente ragione. Definisce una classe "GoodController", contenente una proprietà HttpClient statica che non verrà eliminata; che disobbedisce a quanto
enfatizza un altro esempio nella sezione Esempi : "è necessario chiamare dispose ... quindi l'app non perde risorse".
E, infine, singleton non è privo di sfide.
"Quante persone pensano che la variabile globale sia una buona idea? Nessuno.
Quante persone pensano che singleton sia una buona idea? Alcune.
Cosa dà? I singoli sono solo un gruppo di variabili globali ".
- Citato da questo discorso stimolante, "Global State and Singletons"
PS: SqlConnection
Questo è irrilevante per le attuali domande e risposte, ma è probabilmente una buona conoscenza. Il modello di utilizzo di SqlConnection è diverso. È NON è necessario riutilizzare SqlConnection , perché in grado di gestire il suo pool di connessioni meglio così.
La differenza è causata dal loro approccio all'implementazione. Ogni istanza di HttpClient utilizza il proprio pool di connessioni (citato da
qui ); ma SqlConnection stesso è gestito da un pool di connessioni centrale, secondo questo .
E devi ancora smaltire SqlConnection, come si suppone faccia per HttpClient.