Diretrizes para usar o HttpClient

A classe System.Net.Http.HttpClient envia solicitações HTTP e recebe respostas HTTP de um recurso identificado por um URI. Uma instância HttpClient é uma coleção de configurações aplicadas a todas as solicitações executadas por essa instância e cada instância usa seu próprio pool de conexões, o que isola suas solicitações de outras. A partir do .NET Core 2.1, a classe SocketsHttpHandler fornece a implementação, tornando o comportamento consistente em todas as plataformas.

Comportamento DNS

HttpClient só resolve entradas DNS quando uma conexão é criada. Ele não controla as durações do TTL (tempo de vida útil) especificadas pelo servidor DNS. Se as entradas DNS forem alteradas regularmente, o que pode acontecer em alguns cenários, o cliente não respeitará essas atualizações. Para resolver esse problema, você pode limitar o tempo de vida da conexão definindo a propriedade PooledConnectionLifetime, de modo que a pesquisa DNS seja repetida quando a conexão for substituída. Considere o seguinte exemplo:

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

O HttpClient anterior é configurado para reutilizar conexões por 15 minutos. Depois que o período especificado por PooledConnectionLifetime tiver decorrido, a conexão será fechada e uma nova será criada.

Conexões em pool

O pool de conexões de um HttpClient está vinculado ao SocketsHttpHandler subjacente. Quando a instância HttpClient é descartada, ela descarta todas as conexões existentes dentro do pool. Se, posteriormente, você enviar uma solicitação para o mesmo servidor, uma nova conexão deverá ser recriada. Como resultado, há uma penalidade de desempenho para a criação de conexão desnecessária. Além disso, as portas TCP não são liberadas imediatamente após o fechamento da conexão. (Para obter mais informações sobre isso, confira o TIME-WAIT do TCP no RFC 9293.) Se a taxa de solicitações for alta, o limite de portas disponíveis do sistema operacional poderá estar esgotado. Para evitar problemas de esgotamento de portas, recomendamos reutilizar as instâncias de HttpClient para o maior número possível de solicitações HTTP.

Para resumir o uso recomendado de HttpClient em termos de gerenciamento de tempo de vida, você deve usar clientes de longa duração e configurar o PooledConnectionLifetime (.NET Core e .NET 5+) ou clientes de curta duração criados por IHttpClientFactory.

  • No .NET Core e .NET 5+:

    • Use uma instância de static ou singletonHttpClient com PooledConnectionLifetime configurado para o intervalo desejado, como, por exemplo, 2 minutos, dependendo das alterações de DNS esperadas. Isso resolve os problemas de esgotamento de portas e alterações de DNS sem adicionar a sobrecarga de IHttpClientFactory. Se você precisar simular seu manipulador, poderá registrá-lo separadamente.

    Dica

    Se você usar apenas um número limitado de HttpClient instâncias, essa estratégia também será aceitável. O que importa é que não sejam criadas e descartadas a cada solicitação, já que cada uma delas contém um pool de conexões. O uso de mais de uma instância é necessário para cenários com vários proxies ou para separar contêineres de cookie sem desabilitar completamente a manipulação de cookies.

    • Usando IHttpClientFactory, você pode ter vários clientes configurados de forma diferente para diferentes casos de uso. No entanto, lembre-se de que os clientes criados pela fábrica devem ter vida útil curta e, depois que o cliente for criado, a fábrica não terá mais controle sobre ele.

      A fábrica agrupa instâncias de HttpMessageHandler e, se seu tempo de vida não tiver expirado, um manipulador poderá ser reutilizado do pool quando a fábrica criar uma nova instância de HttpClient. Essa reutilização evita problemas de esgotamento do soquete.

      Se você desejar a capacidade de configuração que o IHttpClientFactory, recomendamos usar a abordagem de cliente com tipo.

  • No .NET Framework, use IHttpClientFactory para gerenciar suas instâncias HttpClient. Se não usar o alocador e, em vez disso, criar por conta própria uma nova instância de cliente para cada solicitação, você poderá esgotar as portas disponíveis.

    Dica

    Se seu aplicativo exigir cookies, considere desabilitar o tratamento automático de cookies ou evitar IHttpClientFactory. Agrupar as instâncias HttpMessageHandler resulta no compartilhamento de objetos CookieContainer. O compartilhamento do objeto CookieContainer de forma inesperada geralmente resulta em código incorreto.

Para obter mais informações sobre como gerenciar o tempo de vida de HttpClient com IHttpClientFactory, confira as diretrizes de IHttpClientFactory.

Resiliência com clientes estáticos

É possível configurar um cliente static ou singleton para usar qualquer número de pipelines de resiliência usando o seguinte padrão:

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

O código anterior:

  • Depende do pacote NuGet Microsoft.Extensions.Http.Resilience.
  • Especifica um manipulador de erros HTTP transitório, configurado com o pipeline de repetição que, com cada tentativa, fará intervalos de atraso de retirada exponencialmente.
  • Define um tempo de vida de conexão em pool de quinze minutos para o socketHandler.
  • Passa o socketHandler para o resilienceHandler com a lógica de repetição.
  • Cria uma instância de um HttpClient dado um resilienceHandler determinado.

Importante

A biblioteca de Microsoft.Extensions.Http.Resilience está marcada como experimental e poderá mudar no futuro.

Confira também