Linee guida per l'uso di HttpClient

La classe System.Net.Http.HttpClient invia richieste HTTP e riceve risposte HTTP da una risorsa identificata da un URI. Un'istanza HttpClient è una raccolta di impostazioni applicate a tutte le richieste eseguite da tale istanza; ogni istanza usa il proprio pool di connessioni, che isola le richieste da altri utenti. A partire da .NET Core 2.1, la classe SocketsHttpHandler fornisce l'implementazione, rendendo coerente il comportamento in tutte le piattaforme.

Comportamento DNS

HttpClient risolve solo le voci DNS quando viene creata una connessione. Non tiene traccia delle durate (TTL) specificate dal server DNS. Se le voci DNS cambiano regolarmente, cosa che può verificarsi in alcuni scenari, il client non rispetterà tali aggiornamenti. Per risolvere questo problema, è possibile limitare la durata della connessione impostando la proprietà PooledConnectionLifetime, in modo che la ricerca DNS venga ripetuta quando la connessione viene sostituita. Si consideri l'esempio seguente:

var handler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15) // Recreate every 15 minutes
};
var sharedClient = new HttpClient(handler);

L'elemento HttpClient precedente è configurato per riutilizzare le connessioni per 15 minuti. Una volta trascorso l'intervallo di tempo specificato da PooledConnectionLifetime e quando la connessione ha completato l'ultima richiesta associata (se presente), questa connessione viene chiusa. Se, in coda, sono presenti richieste in attesa, viene creata una nuova connessione in base alle esigenze.

L'intervallo di 15 minuti è stato scelto arbitrariamente a scopo illustrativo. È preferibile scegliere il valore in base alla frequenza prevista delle modifiche al DNS o di altra rete.

Connessioni in pool

Il pool di connessioni per un oggetto HttpClient è collegato all'oggetto sottostante SocketsHttpHandler. Quando l'istanza HttpClient viene eliminata, elimina tutte le connessioni esistenti all'interno del pool. Se in seguito si invia una richiesta allo stesso server, è necessario ricreare una nuova connessione. Di conseguenza, si verifica una riduzione delle prestazioni per la creazione di connessioni non necessarie. Inoltre, le porte TCP non vengono rilasciate immediatamente dopo la chiusura della connessione. Per altre informazioni, vedere TIME-WAIT TCP in RFC 9293. Se la frequenza delle richieste è elevata, il limite del sistema operativo delle porte disponibili potrebbe venire raggiunto. Per evitare problemi di esaurimento delle porte, è consigliabile riutilizzare le istanze HttpClient per il maggior numero possibile di richieste HTTP.

Per riepilogare l'uso consigliato di HttpClient in termini di gestione della durata, è consigliabile usare client di lunga durata e impostare PooledConnectionLifetime (.NET Core e .NET 5+) o client di breve durata creati da IHttpClientFactory.

  • In .NET Core e .NET 5+:

    • Usare un'istanza static o singletonHttpClient con PooledConnectionLifetime impostato sull'intervallo desiderato, ad esempio 2 minuti, a seconda delle modifiche DNS previste. In questo modo vengono risolti sia i problemi di esaurimento delle porte che quelli di modifiche DNS senza aggiungere il sovraccarico di IHttpClientFactory. Se è necessario essere in grado di simulare il gestore, è possibile registrarlo separatamente.

    Suggerimento

    Se si usa solo un numero limitato di istanze HttpClient, è comunque una strategia accettabile. Ciò che conta è che non vengono creati ed eliminati con ogni richiesta, perché ognuno contiene un pool di connessioni. L'uso di più istanze è necessario per scenari con più proxy o per separare i contenitori di cookie senza disabilitare completamente la gestione dei cookie.

    • Usando IHttpClientFactory, è possibile avere più client configurati in modo diverso per casi d'uso diversi. Tenere tuttavia presente che i client creati dalla factory devono essere di breve durata e che, dopo la creazione del client, la factory non ha più il controllo su di essi.

      Le istanze HttpMessageHandler del pool di factory e, se la durata non è scaduta, un gestore può essere riutilizzato dal pool quando la factory crea una nuova istanza HttpClient. Questo riutilizzo evita eventuali problemi di esaurimento del socket.

      Se si desidera la configurabilità fornita da IHttpClientFactory, è consigliabile usare l'approccio tipizzato-client.

  • In .NET Framework usare IHttpClientFactory per gestire le istanze HttpClient. Se non si usa la factory e si crea invece una nuova istanza client per ogni richiesta manualmente, è possibile esaurire le porte disponibili.

    Suggerimento

    Se l'app richiede cookie, è consigliabile disabilitare la gestione automatica dei cookie o evitare IHttpClientFactory. Il pooling delle istanze HttpMessageHandler comporta la condivisione di oggetti CookieContainer. La condivisione di oggetti CookieContainer imprevisti comporta spesso codice non corretto.

Per altre informazioni sulla gestione della durata di HttpClient con IHttpClientFactory, vedere le IHttpClientFactorylinee guida.

Resilienza con client statici

È possibile configurare un client static o singleton per usare un numero qualsiasi di pipeline di resilienza tramite il modello seguente:

using System;
using System.Net.Http;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Http.Resilience;
using Polly;

var retryPipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddRetry(new HttpRetryStrategyOptions
    {
        BackoffType = DelayBackoffType.Exponential,
        MaxRetryAttempts = 3
    })
    .Build();

var socketHandler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15)
};
var resilienceHandler = new ResilienceHandler(retryPipeline)
{
    InnerHandler = socketHandler,
};

var httpClient = new HttpClient(resilienceHandler);

Il codice precedente:

  • Si basa sul pacchetto NuGet Microsoft.Extensions.Http.Resilience.
  • Specifica un gestore degli errori HTTP temporanei, configurato con la pipeline di retry che con ogni tentativo ritarderà in modo esponenziale gli intervalli di ritardo del backoff.
  • Definisce una durata di connessione in pool di quindici minuti per socketHandler.
  • Passa socketHandler a resilienceHandler con la logica di retry.
  • Crea un'istanza di un HttpClient in base a resilienceHandler.

Importante

La libreria Microsoft.Extensions.Http.Resilience è attualmente contrassegnata come sperimentale e potrebbe cambiare in futuro.

Vedi anche