Compartilhar via


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 acompanha as durações 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, limite o tempo de vida da conexão definindo a propriedade, de modo que a PooledConnectionLifetime pesquisa de DNS seja repetida quando a conexão for substituída. Considere este 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 pelo PooledConnectionLifetime decorrido e a conexão tiver concluído sua última solicitação associada (se houver), a conexão será fechada. Se houver alguma solicitação aguardando na fila, uma nova conexão será criada conforme necessário.

O intervalo de 15 minutos foi escolhido arbitrariamente para fins ilustrativos. Você deve escolher o valor com base na frequência esperada de DNS ou outras alterações de rede.

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á um impacto no desempenho devido à criação de conexões desnecessárias. 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 do ciclo de vida, você deve usar clientes de longa vida e configurar o PooledConnectionLifetime (.NET Core e .NET 5+), ou usar clientes de curta vida criados por IHttpClientFactory.

  • No contexto do .NET Core e .NET 5+:

    • Use uma instância de static ou singletonHttpClient com PooledConnectionLifetime definida para o intervalo desejado, como 2 minutos, de acordo com as 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 elas não sejam criadas e descartadas a cada solicitação, já que cada uma contém um pool de conexão. 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 o seu tempo de vida não tiver expirado, um manipulador poderá ser reutilizado do pool de instâncias quando a fábrica criar uma nova instância de HttpClient. Essa reutilização evita problemas de esgotamento do soquete.

      Se você desejar a configurabilidade que o IHttpClientFactory fornece, recomendamos usar a abordagem de cliente tipado.

  • No contexto do .NET Framework, use IHttpClientFactory para gerenciar suas instâncias de HttpClient. Se você não usar a fábrica e, em vez disso, criar você mesmo uma nova instância de cliente para cada solicitação, pode esgotar as portas disponíveis.

    Aviso

    Se o seu aplicativo exigir cookies, é recomendável evitar o uso de IHttpClientFactory. Agrupar as instâncias HttpMessageHandler resulta no compartilhamento de objetos CookieContainer. O compartilhamento imprevisto CookieContainer pode vazar cookies entre partes não relacionadas do aplicativo. Além disso, quando HandlerLifetime expira, o handler é reciclado, o que significa que todos os cookies armazenados em seu CookieContainer são apagados.

Para obter mais informações sobre como gerenciar o tempo de vida de HttpClient com IHttpClientFactory, consulte 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 Microsoft.Extensions.Http.Resilience;
using Polly;

class MyClass
{
    static HttpClient? s_httpClient;

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

        s_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 um pipeline de repetição que, em cada tentativa, terá intervalos de espera com recuo exponencial.
  • 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 tentativa.
  • Cria uma instância de HttpClient compartilhada dado o resilienceHandler.

Confira também