HttpClient e HttpClientHandler devono essere disposti tra le richieste?


334

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

La usingdocumentazione della dichiarazione 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()né esplicitamente né 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 di HttpClient. Ho usato tutte le istanze di HttpClient con l'utilizzo dell'istruzione sulla mia app e ho pensato che fosse la strada giusta poiché HttpClient implementa l'interfaccia IDisposable. Sono sulla buona strada?

La sua risposta fu:

In generale questo è corretto, anche se devi stare attento con "utilizzo" e asincrono in quanto non si mescolano realmente in .Net 4, In .Net 4.5 puoi usare "wait" all'interno di un'istruzione "using".

A proposito, puoi riutilizzare lo stesso HttpClient quante volte [come] ti piace, quindi in genere non li creerai / eliminerai sempre.

Il secondo paragrafo è superfluo a questa domanda, che non si preoccupa di quante volte è possibile utilizzare un'istanza di HttpClient, ma di se è necessario smaltirla dopo che non è più necessaria.

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

Quindi le mie domande sono:

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

  2. Se non fosse necessario, sarebbe comunque una "buona pratica", poiché implementano IDisposable?

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

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

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


2
@Damien_The_Unbeliever, grazie per il tuo feedback. Hai qualche suggerimento su come potrei chiarire la domanda? Voglio sapere se può portare a problemi generalmente associati alla non eliminazione delle risorse, come la perdita di risorse e la corruzione dei dati.
Fernando Correia,

9
@Damien_The_Unbeliever: non vero. In particolare, gli autori di stream devono essere disposti ad avere un comportamento corretto.
Stephen Cleary,

1
@StephenCleary: a quali aspetti stai pensando? Certamente, puoi fare appello Flusha uno dopo ogni scrittura e, a parte l'inconveniente che continua a trattenere le risorse sottostanti più a lungo del necessario, cosa non accadrà che è richiesto per un "comportamento corretto"?
Damien_The_Unbeliever

1
Questo è chiaramente sbagliato: "Di norma, quando si utilizza un oggetto IDisposable, è necessario dichiararlo e creare un'istanza in un'istruzione using". Leggerei sempre la documentazione sulla classe che implementa IDisposable prima di decidere se usare un uso per esso. Come autore di librerie in cui implemento IDisposable perché è necessario rilasciare risorse non modificate, sarei inorridito se i consumatori creassero un'istanza ogni volta invece di riutilizzare un'istanza esistente. Questo non vuol dire che alla fine non gettare l'istanza ..
markmnl

1
Ho inviato una PR a microsoft per aggiornare i loro documenti: github.com/dotnet/docs/pull/2470
markmnl

Risposte:


259

Il consenso generale è che non è necessario (non è 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: la ricerca per indicizzazione di HttpClient provoca perdite di memoria come riferimento.

Consiglio vivamente anche di leggere il capitolo HttpClient da Progettazione di API Web evolvibili con ASP.NET per un contesto su ciò che sta accadendo sotto il cofano, in particolare la sezione "Ciclo di vita" citata qui:

Sebbene HttpClient implementi indirettamente l'interfaccia IDisposable, l'utilizzo standard di HttpClient non è quello di eliminarlo dopo ogni richiesta. L'oggetto HttpClient è destinato a durare fino a quando l'applicazione deve effettuare richieste HTTP. La presenza di un oggetto su più richieste consente di impostare DefaultRequestHeaders e ti impedisce di dover reimpostare cose come CredentialCache e CookieContainer su ogni richiesta, come era necessario con HttpWebRequest.

O anche aprire DotPeek.


64
Per chiarire la tua risposta, sarebbe corretto affermare che "non è necessario disporre di HttpClient SE TI TENGA ATTENTATO ALL'ISTANZA PER RIUTILIZZARLO DOPO"? Ad esempio, se un metodo viene chiamato ripetutamente e crea una nuova istanza di HttpClient (anche se nella maggior parte dei casi non è il modello consigliato), sarebbe comunque corretto affermare che questo metodo non dovrebbe disporre l'istanza (che non verrà riutilizzata)? Potrebbe portare a migliaia di istanze non presentate. In altre parole, che dovresti provare a riutilizzare le istanze, ma se non le riutilizzi, faresti meglio a smaltirle (per rilasciare le connessioni)?
Fernando Correia,

8
Penso che la risposta comprensibilmente frustrante ma corretta sia che dipende. Se dovessi essere bloccato per dare consigli generali che funzionavano nella maggior parte dei casi (non dico mai tutti), suggerirei di utilizzare un contenitore IoC e registrare un'istanza di HttpClient come singleton. La durata dell'istanza verrebbe quindi portata a quella della durata del contenitore. Questo potrebbe essere definito a livello di applicazione o forse per richiesta in un'applicazione web.
David Peden,

25
@FernandoCorreia Sì. Se per qualche motivo si creano e distruggono ripetutamente istanze di HttpClient, sì, è necessario eliminarlo. Non sto suggerendo di ignorare l'interfaccia IDisposable, sto solo cercando di incoraggiare le persone a riutilizzare le istanze.
Darrel Miller,

20
Solo per aggiungere ulteriore credibilità a questa risposta, ho parlato oggi con il team di HttpClient e hanno confermato che HttpClient non è stato progettato per essere utilizzato per richiesta. Un'istanza di HttpClient deve essere mantenuta attiva mentre un'applicazione client continua a interagire con un determinato host.
Darrel Miller,

19
@DavidPeden Registrare HttpClient come singleton mi sembra pericoloso perché è mutevole. Ad esempio, tutti coloro che assegnano alla Timeoutproprietà non si calpesterebbero l'un l'altro?
Jon-Eric,

47

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

  1. In generale, la maggior parte degli IDisposableoggetti dovrebbe idealmente essere eliminata quando hai finito con loro , specialmente quelli che possiedono risorse del sistema operativo denominate / condivise . HttpClientnon fa eccezione, dal momento che come Darrel Miller sottolinea , alloca token di annullamento e i corpi di richiesta / risposta possono essere flussi non gestiti.
  2. Tuttavia, la migliore pratica 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 eliminerai mai semplicemente perché ne avrai bisogno in ogni momento .
  3. Il problema con il riutilizzo dello stesso HttpClient "per sempre" è che la connessione HTTP sottostante potrebbe rimanere aperta contro l'IP con risoluzione DNS originale, indipendentemente dalle modifiche DNS . Questo può essere un problema in scenari come la distribuzione blu / verde e il failover basato su DNS . Esistono vari approcci per affrontare questo problema, il più affidabile che coinvolge il server che invia Connection:closeun'intestazione dopo aver apportato modifiche al DNS. Un'altra possibilità consiste nel riciclare il HttpClientlato client, periodicamente o tramite qualche meccanismo che apprende sulla modifica del DNS. Vedi https://github.com/dotnet/corefx/issues/11224 per maggiori informazioni (ti suggerisco di leggerlo attentamente prima di usare ciecamente il codice suggerito nel post sul blog collegato).

Lo dispongo sempre perché non riesco a cambiare proxy su un'istanza;)
ed22

Se è necessario disporre di HttpClient per qualsiasi motivo, è necessario mantenere un'istanza statica di HttpMessageHandler, poiché eliminando quella è effettivamente la causa dei problemi attribuiti allo smaltimento di HttpClient. HttpClient presenta un sovraccarico del costruttore che consente di specificare che il gestore fornito non deve essere eliminato, nel qual caso è possibile riutilizzare HttpMessageHandler con altre istanze di HttpClient.
Tom Lint,

Dovresti tenere duro su HttpClient, ma puoi usare qualcosa come System.Net.ServicePointManager.DnsRefreshTimeout = 3000; Ciò è utile, ad esempio, se si utilizza un dispositivo mobile che può passare in qualsiasi momento tra wifi e 4G.
Johan Franzén,

18

A mio avviso, la chiamata Dispose()è necessaria solo quando sta bloccando le risorse necessarie in un secondo momento (come una connessione specifica). Si consiglia sempre di liberare risorse che non si utilizzano più, anche se non sono più necessarie, semplicemente perché in genere non si dovrebbe trattenere risorse che non si utilizzano (gioco di parole).

L'esempio di Microsoft non è errato, necessariamente. Tutte le risorse utilizzate verranno rilasciate all'uscita dall'applicazione. E nel caso di quell'esempio, ciò accade quasi immediatamente dopo aver HttpClientfinito di essere utilizzato. In casi simili, la chiamata esplicita Dispose()è in qualche modo superflua.

Ma, in generale, quando una classe implementa IDisposable, la comprensione è che dovresti Dispose()delle sue istanze non appena sei completamente pronto e capace. Immagino che ciò sia particolarmente vero in casi in HttpClientcui non è esplicitamente documentato se le risorse o le connessioni siano mantenute / aperte. Nel caso in cui la connessione verrà riutilizzata di nuovo [presto], ti consigliamo di rinunciarvi Dipose()- non sei "completamente pronto" in quel caso.

Vedi anche: IDisposable.Dispose Method e Quando chiamare Dispose


7
È come se qualcuno porta una banana a casa tua, la mangia e se ne sta con la buccia. Cosa dovrebbero fare con la buccia? ... Se stanno uscendo dalla porta, lasciali andare. Se stanno attaccando, falli buttare nella spazzatura in modo che non puzzi il posto.
svidgen,

solo per chiarire questa risposta, stai dicendo "non è necessario disporre se il programma finirà subito dopo averlo usato"? E che dovresti smaltire se il programma dovrebbe continuare per qualche tempo a fare altre cose?
Fernando Correia,

@FernandoCorreia Sì, a meno che non dimentichi qualcosa, penso che sia un principio sicuro. Pensaci comunque in ogni caso. Se stai lavorando con una connessione, ad esempio, non vuoi Dispose()farlo prematuramente e devi riconnetterti qualche secondo dopo se la connessione esistente è riutilizzabile. Allo stesso modo, non vuoi inutilmente Dispose()immagini o altre strutture che potresti dover ricostruire in un minuto o due.
svidgen,

Capisco. Ma nel caso particolare di HttpClient e HttpClientHandler di cui tratta questa domanda, tengono aperta una risorsa come una connessione HTTP? Se è quello che sta succedendo, potrei dover ripensare il mio modello di utilizzo.
Fernando Correia,

1
@DPeden La tua risposta non è affatto in conflitto con la mia. Nota, ho detto, dovresti disporre () delle sue istanze non appena sei completamente pronto e capace . Se prevedi di utilizzare nuovamente l'istanza, non sei pronto .
svidgen,

9

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

HttpClientHandler.cs - Smaltisci

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Se non si chiama dispose, ServicePointManager.MaxServicePointIdleTime, che viene eseguito da un timer, chiuderà le connessioni http. L'impostazione predefinita è 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 non chiamare dispose e lasciare che il timer di connessione inattiva inizi e chiuda le connessioni, anche se sarebbe meglio chiamare dispose in un'istruzione using se sai che hai finito con un'istanza di HttpClient e liberare le risorse più velocemente.



8

Risposta breve: No, l'affermazione nella risposta attualmente accettata NON è accurata : "Il consenso generale è che non è necessario (non si deve) disporre di HttpClient".

Risposta lunga : ENTRAMBE le seguenti affermazioni sono vere e realizzabili allo stesso tempo:

  1. "HttpClient deve essere istanziato una volta e riutilizzato per tutta la durata di un'applicazione", citato dalla documentazione ufficiale .
  2. Si IDisposablesuppone / si consiglia di eliminare un oggetto.

E NON SI CONFLITANO NECESSARAMENTE l'uno con l'altro. È solo una questione di come organizzi il tuo codice per riutilizzarloHttpClient AND e comunque eliminarlo correttamente.

Una risposta ancora più lunga citata dalla mia altra risposta :

Non è una coincidenza vedere persone in alcuni post del blog incolpare il modo in cui HttpClientl’ IDisposableinterfaccia fa sì che tendano ad usare ilusing (var client = new HttpClient()) {...} modello e quindi a causare un problema di gestore di socket esaurito.

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 maiIDisposable oggetti che devono essere 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 alle esigenze 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 HttpClientuna volta ma non la smaltisce mai, ecco perché possiamo vedere dal suo output netstat che, la connessione rimane allo stato STABILITO, il 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.


grazie per questa spiegazione. È chiaramente fatto luce sul consenso. Secondo la tua opinione, quando pensi che sia giusto chiamare HttpClient.Dispose?
Jeson Martajaya,

@JesonMartajaya, eliminalo quando l'applicazione non deve più utilizzare l'istanza httpClient. Potresti pensare che tale suggerimento sembri vago, ma in realtà può allinearsi perfettamente con il ciclo di vita della tua HttpClient clientvariabile, che è una cosa di Programmazione-101 che presumibilmente stai già facendo comunque. Potresti anche essere ancora in grado di utilizzare using (...) {...}anche. Ad esempio, vedi l'esempio Hello World nella mia risposta.
RayLuo,

7

Dal momento che non sembra che nessuno l'abbia menzionato ancora qui, il nuovo modo migliore per gestire HttpClient e HttpClientHandler in .Net Core 2.1 sta usando HttpClientFactory .

Risolve la maggior parte delle problematiche e dei problemi sopra citati in modo pulito e facile da usare. Dal grande post sul blog di Steve Gordon :

Aggiungi i seguenti pacchetti al tuo progetto .Net Core (2.1.1 o successivo):

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

Aggiungi questo a Startup.cs:

services.AddHttpClient();

Iniettare e usare:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

Esplora la serie di post nel blog di Steve per molte altre funzionalità.


4

Nel mio caso, stavo creando un HttpClient all'interno di un metodo che ha effettivamente effettuato 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 eliminare HttpClient), alla fine fallirebbe SocketException (tentativo di connessione fallito).

Ho reso HttpClient una variabile di istanza (disponendola a livello di classe) e il problema è andato via. Quindi direi di sì, smaltisci HttpClient, supponendo che sia sicuro (non hai chiamate asincrone eccezionali) per farlo.


Grazie per il feedback. Questo è un problema piuttosto complesso. Consiglio di leggere gli articoli collegati nella risposta di DPeden. In breve, l'istanza di HttpClient deve essere riutilizzata per tutto il ciclo di vita dell'applicazione. Se si creano ripetutamente nuove istanze, potrebbe essere necessario eliminarle.
Fernando Correia,

6
"l'istanza di HttpClient deve essere riutilizzata per tutto il ciclo di vita dell'applicazione", questa non è una buona idea con molte applicazioni. Sto pensando alle applicazioni web che usano HttpClient. HttpClient mantiene lo stato (ad esempio le intestazioni di richiesta che utilizzerà), quindi un thread di richieste Web potrebbe facilmente calpestare ciò che sta facendo un altro. Nelle applicazioni Web di grandi dimensioni ho anche visto HttpClient come il problema dei principali problemi di connessione. In caso di dubbio dico Smaltisci.
bytedev,

@nashwan non puoi cancellare le intestazioni e aggiungerne di nuove prima di ogni richiesta?
Mandeep Janjua,

Microsoft consiglia inoltre di riutilizzare le istanze di HttpClient
docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/…

@MandeepJanjua quell'esempio sembra essere un client come un'applicazione console. Mi riferivo a un'applicazione web come client.
bytedev,

3

Nell'uso tipico (risposte <2 GB) non è necessario disporre i messaggi HttpResponse.

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

  • Se si stanno leggendo i dati in un byte [] (ad esempio GetByteArrayAsync) o in una stringa, tutti i dati vengono letti, quindi non è necessario eliminarli.
  • Per impostazione predefinita, gli altri overload leggono lo Stream fino a 2 GB (HttpCompletionOption è ResponseContentRead, HttpClient.MaxResponseContentBufferLa dimensione predefinita è 2 GB)

Se si imposta HttpCompletionOption su ResponseHeadersRead o la risposta è maggiore di 2 GB, è necessario ripulire. Questo può essere fatto chiamando Dispose su HttpResponseMessage o chiamando Dispose / Close sullo Stream ottenuto dal contenuto di HttpResonseMessage o leggendo il contenuto completamente.

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


2

Se si desidera eliminare HttpClient, è possibile impostarlo come pool di risorse. E alla fine della tua applicazione, elimini il tuo pool di risorse.

Codice:

// Notice that IDisposable is not implemented here!
public interface HttpClientHandle
{
    HttpRequestHeaders DefaultRequestHeaders { get; }
    Uri BaseAddress { get; set; }
    // ...
    // All the other methods from peeking at HttpClient
}

public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable
{
    public static ConditionalWeakTable<Uri, HttpClientHander> _httpClientsPool;
    public static HashSet<Uri> _uris;

    static HttpClientHander()
    {
        _httpClientsPool = new ConditionalWeakTable<Uri, HttpClientHander>();
        _uris = new HashSet<Uri>();
        SetupGlobalPoolFinalizer();
    }

    private DateTime _delayFinalization = DateTime.MinValue;
    private bool _isDisposed = false;

    public static HttpClientHandle GetHttpClientHandle(Uri baseUrl)
    {
        HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl);
        _uris.Add(baseUrl);
        httpClient._delayFinalization = DateTime.MinValue;
        httpClient.BaseAddress = baseUrl;

        return httpClient;
    }

    void IDisposable.Dispose()
    {
        _isDisposed = true;
        GC.SuppressFinalize(this);

        base.Dispose();
    }

    ~HttpClientHander()
    {
        if (_delayFinalization == DateTime.MinValue)
            _delayFinalization = DateTime.UtcNow;
        if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout)
            GC.ReRegisterForFinalize(this);
    }

    private static void SetupGlobalPoolFinalizer()
    {
        AppDomain.CurrentDomain.ProcessExit +=
            (sender, eventArgs) => { FinalizeGlobalPool(); };
    }

    private static void FinalizeGlobalPool()
    {
        foreach (var key in _uris)
        {
            HttpClientHander value = null;
            if (_httpClientsPool.TryGetValue(key, out value))
                try { value.Dispose(); } catch { }
        }

        _uris.Clear();
        _httpClientsPool = null;
    }
}

var handler = HttpClientHander.GetHttpClientHandle (nuovo Uri ("base url")).

  • HttpClient, come interfaccia, non può chiamare Dispose ().
  • Dispose () verrà chiamato in modo ritardato dal Garbage Collector. O quando il programma pulisce l'oggetto attraverso il suo distruttore.
  • Utilizza riferimenti deboli + logica di pulizia ritardata in modo che rimanga in uso fino a quando viene riutilizzato frequentemente.
  • Alloca solo un nuovo HttpClient per ogni URL di base passato ad esso. I motivi spiegati da Ohad Schneider rispondono di seguito. Comportamento errato durante la modifica dell'URL di base.
  • HttpClientHandle consente di deridere nei test

Perfetto. Vedo che si chiama Disposeil metodo che si registra su GC. Questo dovrebbe essere valutato più in alto nella parte superiore.
Jeson Martajaya,

Si noti che HttpClient esegue il pool di risorse per URL di base. Quindi, se stai colpendo migliaia di siti Web diversi in un elenco, le tue prestazioni peggioreranno senza ripulire quei singoli siti. Questo espone la capacità di disporre di ogni url base. Tuttavia, se si utilizza un solo sito Web, potrebbe essere solo per motivi accademici chiamare smaltimento.
TamusJRoyce,

1

L'uso dell'iniezione delle dipendenze nel costruttore HttpClientsemplifica la gestione della durata della vita , portando la gestione della vita fuori dal codice che ne ha bisogno e rendendola facilmente modificabile in un secondo momento.

La mia attuale preferenza è quella di creare una classe client http separata che erediti da HttpClientuna volta per dominio endpoint di destinazione e quindi renderlo un singleton usando l'iniezione delle dipendenze.public class ExampleHttpClient : HttpClient { ... }

Quindi prendo una dipendenza del costruttore dal client http personalizzato nelle classi di servizio in cui ho bisogno di accedere a quell'API. Ciò risolve il problema della durata e presenta vantaggi in termini di pool di connessioni.

Puoi vedere un esempio funzionante nella relativa risposta su https://stackoverflow.com/a/50238944/3140853



-2

Penso che si dovrebbe usare il modello singleton per evitare di dover creare istanze di HttpClient e chiuderlo continuamente. Se si utilizza .Net 4.0 è possibile utilizzare un codice di esempio come di seguito. per ulteriori informazioni sul modello singleton controlla qui .

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 di seguito.

var client = HttpClientSingletonWrapper.Instance;

3
Qualcosa a cui prestare attenzione quando si fa questo (e altri sistemi analoghi): " I membri di istanza non sono garantiti per essere thread-safe. "
TNE

2
Il fatto che questa risposta sia corretta o no dovrebbe dipendere totalmente dall'applicazione da cui si desidera utilizzare HttpClient. Se si dispone di un'applicazione Web e si crea un HttpClient singleton che tutte le richieste Web condivideranno, potenzialmente si otterranno molte eccezioni di connessione (a seconda della popolarità del sito Web! :-)). (Vedi la risposta di David Faivre)
bytedev
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.