c# too - HttpClient e HttpClientHandler devono essere eliminati?




slow dispose (8)

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?


Answers

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;

Nel mio caso, stavo creando un HttpClient all'interno di un metodo che effettivamente ha fatto la chiamata di servizio. Qualcosa di simile a:

public void DoServiceCall() {
  var client = new HttpClient();
  await client.PostAsync();
}

In un ruolo di lavoro di Azure, dopo aver ripetutamente chiamato questo metodo (senza disporre di HttpClient), alla fine avrebbe avuto esito negativo con SocketException (tentativo di connessione non riuscito).

Ho reso HttpClient una variabile di istanza (disponendola a livello di classe) e il problema è andato via. Quindi direi di sì, disporre HttpClient, assumendo la sua sicurezza (non hai chiamate asincrone in sospeso) per farlo.


Le risposte attuali sono un po 'confuse e fuorvianti e mancano alcune importanti implicazioni DNS. Cercherò di riassumere dove le cose stanno chiaramente.

  1. In generale, la maggior parte degli oggetti IDisposable dovrebbe essere disposta idealmente quando si è finiti con loro , specialmente quelli che possiedono risorse OS nominate / condivise . HttpClient non fa eccezione, dal momento che come sottolinea Darrel Miller , alloca i token di cancellazione e gli organismi di richiesta / risposta possono essere flussi non gestiti.
  2. Tuttavia, la best practice per HttpClient dice che dovresti creare un'istanza e riutilizzarla il più possibile (usando i suoi membri thread-safe in scenari multi-thread). Pertanto, nella maggior parte degli scenari non lo potrai mai disporre semplicemente perché ne avrai sempre bisogno .
  3. Il problema con il riutilizzo dello stesso HttpClient "per sempre" è che la connessione HTTP sottostante potrebbe rimanere aperta contro l'IP risolto in origine, indipendentemente dalle modifiche DNS . Questo può essere un problema in scenari come la distribuzione blu / verde e il failover basato su DNS . Esistono diversi approcci per affrontare questo problema, il più affidabile che coinvolge il server che invia una Connection:close intestazione dopo che si sono verificati i cambiamenti DNS. Un'altra possibilità consiste nel riciclare HttpClient sul lato client, periodicamente o tramite un meccanismo che impara a conoscere la modifica del DNS. Vedere https://github.com/dotnet/corefx/issues/11224 per ulteriori informazioni (suggerisco di leggerlo attentamente prima di utilizzare ciecamente il codice suggerito nel post del blog collegato).

Il consenso generale è che non è (non dovrebbe) necessario disporre di HttpClient.

Molte persone che sono intimamente coinvolte nel modo in cui funziona lo hanno affermato.

Vedere il post sul blog di Darrel Miller e un post SO correlato: i risultati della scansione HttpClient nella perdita di memoria per riferimento.

Suggerisco inoltre vivamente di leggere il capitolo HttpClient dalla progettazione di API Web Evolvable con ASP.NET per il contesto su ciò che sta succedendo sotto la copertura, in particolare la sezione "Ciclo di vita" citata qui:

Sebbene HttpClient implementa indirettamente l'interfaccia IDisposable, l'utilizzo standard di HttpClient non è quello di eliminarlo dopo ogni richiesta. L'oggetto HttpClient è destinato a rimanere attivo finché l'applicazione deve effettuare richieste HTTP. L'esistenza di un oggetto su più richieste abilita un posto per l'impostazione di DefaultRequestHeaders e impedisce di dover specificare nuovamente cose come CredentialCache e CookieContainer su ogni richiesta, come era necessario con HttpWebRequest.

O anche aprire DotPeek.


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.


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.


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


    GRV.DataSource = Class1.DataTable;
            GRV.DataBind();

Class1.GRV.Rows[e.RowIndex].Delete();
        GRV.DataSource = Class1.DataTable;
        GRV.DataBind();




c# .net-4.5 idisposable using dotnet-httpclient