[c#] HttpClient e HttpClientHandler devono essere eliminati?



3 Answers

A mio avviso, chiamare Dispose() è necessario solo quando si bloccano le risorse di cui si ha bisogno in seguito (come una particolare connessione). Si consiglia sempre di liberare risorse che non si utilizzano più, anche se non ne hai più bisogno, semplicemente perché generalmente non dovresti tenere le risorse che non stai utilizzando (gioco di parole).

L'esempio di Microsoft non è errato, necessariamente. Tutte le risorse utilizzate verranno rilasciate all'uscita dell'applicazione. E nel caso di quell'esempio, ciò accade quasi immediatamente dopo l' HttpClient . In casi simili, chiamare esplicitamente Dispose() è in qualche modo superfluo.

Ma, in generale, quando una classe implementa IDisposable , l'intesa è che dovresti Dispose() delle sue istanze non appena sarai pronto e capace. Direi che questo è particolarmente vero in casi come HttpClient cui non è esplicitamente documentato se le risorse o le connessioni vengono mantenute su / open. Nel caso in cui la connessione verrà riutilizzata di nuovo [presto], ti consigliamo di rinunciare a Dipose() ing - non sei "completamente pronto" in quel caso.

Vedi anche: Metodo IDisposable.Dispose e Quando chiamare Dispose

Question

System.Net.Http.HttpClient e System.Net.Http.HttpClientHandler in .NET Framework 4.5 implementano IDisposable (tramite System.Net.Http.HttpMessageInvoker ).

La documentazione di dichiarazione using dice:

Di norma, quando si utilizza un oggetto IDisposable, è necessario dichiararlo e creare un'istanza in un'istruzione using.

Questa risposta utilizza questo modello:

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();
}

Ma gli esempi più visibili di Microsoft non chiamano Dispose() esplicitamente o implicitamente. Per esempio:

Nei commenti dell'annuncio , qualcuno ha chiesto al dipendente Microsoft:

Dopo aver controllato i tuoi campioni, ho visto che non hai eseguito l'azione di eliminazione sull'istanza HttpClient. Ho usato tutte le istanze di HttpClient con l'utilizzo dell'istruzione sulla mia app e ho pensato che fosse la giusta via da quando HttpClient implementa l'interfaccia IDisposable. Sono sulla buona strada?

La sua risposta è stata:

In generale è corretto, anche se bisogna stare attenti con "usare" e asincrono come non si confondono realmente. Net 4, In. Net 4.5 è possibile utilizzare "attendere" all'interno di una istruzione "using".

A proposito, puoi riutilizzare lo stesso HttpClient tutte le volte che [come] ti piacciono, quindi di solito non le creerai / disporrai tutto il tempo.

Il secondo paragrafo è superfluo a questa domanda, che non riguarda il numero di volte in cui è possibile utilizzare un'istanza HttpClient, ma se è necessario eliminarlo dopo averlo non più necessario.

(Aggiornamento: in effetti quel secondo paragrafo è la chiave per la risposta, come indicato di seguito da @DPeden.)

Quindi le mie domande sono:

  1. È necessario, vista l'attuale implementazione (.NET Framework 4.5), chiamare Dispose () su istanze HttpClient e HttpClientHandler? Chiarimento: con "necessario" intendo se ci sono conseguenze negative per il mancato smaltimento, come la perdita di risorse o rischi di corruzione dei dati.

  2. Se non è necessario, sarebbe comunque una "buona pratica", dal momento che implementano IDisposable?

  3. Se è necessario (o consigliato), questo codice menzionato sopra lo implementa in modo sicuro (per .NET Framework 4.5)?

  4. Se queste classi non richiedono la chiamata Dispose (), perché sono state implementate come IDisposable?

  5. Se richiedono, o se è una pratica consigliata, gli esempi Microsoft sono fuorvianti o non sicuri?




Dispose () chiama il codice sottostante, che chiude le connessioni aperte dall'istanza HttpClient. Il codice è stato creato decompilando con dotPeek.

HttpClientHandler.cs - Dispose

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Se non si chiama dispose, ServicePointManager.MaxServicePointIdleTime, che viene eseguito da un timer, chiuderà le connessioni http. Il valore predefinito è 100 secondi.

ServicePointManager.cs

internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback);
private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000);

private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
{
  ServicePoint servicePoint = (ServicePoint) context;
  if (Logging.On)
    Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode()));
  lock (ServicePointManager.s_ServicePointTable)
    ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString);
  servicePoint.ReleaseAllConnectionGroups();
}

Se non hai impostato il tempo di inattività su infinito allora sembra sicuro di non chiamare il dispose e lasciare che il timer di connessione inattiva entri in gioco e chiuda le connessioni, anche se sarebbe meglio per te chiamare in una dichiarazione using se sai che hai finito con un'istanza HttpClient e libera le risorse più velocemente.




Penso che uno dovrebbe usare il modello singleton per evitare di dover creare istanze di HttpClient e chiuderlo tutto il tempo. Se stai usando .Net 4.0 potresti usare un codice di esempio come sotto. per ulteriori informazioni sul controllo del modello singleton here .

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

Usa il codice come sotto.

var client = HttpClientSingletonWrapper.Instance;



Nell'uso tipico (risposte <2 GB) non è necessario eliminare gli HttpResponseMessages.

I tipi di restituzione dei metodi HttpClient devono essere eliminati se il loro contenuto di flusso non è completamente letto. Altrimenti non è possibile per il CLR sapere che gli Stream possono essere chiusi fino a quando non vengono raccolti.

  • Se stai leggendo i dati in un byte [] (es. GetByteArrayAsync) o una stringa, tutti i dati vengono letti, quindi non c'è bisogno di disporre.
  • Gli altri sovraccarichi visualizzeranno automaticamente il flusso fino a 2 GB (HttpCompletionOption è ResponseContentRead, HttpClient.MaxResponseContentBufferSize predefinito è 2 GB)

Se si imposta HttpCompletionOption su ResponseHeadersRead o la risposta è superiore a 2 GB, è necessario eseguire la pulizia. Questo può essere fatto chiamando Dispose su HttpResponseMessage o chiamando Dispose / Close sul flusso ottenuto dal contenuto HttpResonseMessage o leggendo il contenuto completamente.

Sia che chiami Dispose su HttpClient dipende dal fatto che desideri annullare le richieste in sospeso o meno.




Related