Directrices para usar HttpClient

La clase System.Net.Http.HttpClient envía solicitudes HTTP y recibe respuestas HTTP de un recurso identificado mediante un URI. Una instancia HttpClient es una colección de configuraciones que se aplican a todas las solicitudes ejecutadas por esa instancia, y cada instancia usa su propio grupo de conexiones, lo cual aísla sus solicitudes de otras. A partir de .NET Core 2.1, la clase SocketsHttpHandler proporciona la implementación, lo que hace que el comportamiento sea coherente en todas las plataformas.

Comportamiento de DNS

HttpClient solo resuelve las entradas DNS cuando se crea una conexión. No realiza ningún seguimiento de las duraciones de período de vida (TTL) especificadas por el servidor DNS. Si las entradas DNS cambian con regularidad, lo que puede ocurrir en algunos escenarios, el cliente no respetará esas actualizaciones. Para resolver este problema, puede limitar la duración de la conexión mediante la configuración de la propiedad PooledConnectionLifetime, de modo que la búsqueda de DNS sea obligatoria cuando se reemplace la conexión. Considere el ejemplo siguiente:

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

La clase HttpClient anterior se configura para reutilizar las conexiones durante 15 minutos. Una vez transcurrido el intervalo de tiempo especificado por PooledConnectionLifetime y cuando la conexión haya completado su última solicitud asociada (si la hubiera), esta conexión se cierra. Si hay solicitudes esperando en la cola, se crea una nueva conexión según sea necesario.

El intervalo de 15 minutos se eligió arbitrariamente con fines ilustrativos. Debe elegir el valor en función de la frecuencia esperada del DNS u otros cambios de red.

Conexiones agrupadas

El grupo de conexiones de una clase HttpClient está vinculado a la clase SocketsHttpHandler subyacente. Cuando se elimina la instancia HttpClient, se eliminan todas las conexiones existentes dentro del grupo. Si posteriormente envía una solicitud al mismo servidor, se vuelve a crear una nueva conexión. Como resultado, hay una penalización de rendimiento por la creación de conexiones innecesarias. Además, los puertos TCP no se liberan inmediatamente después del cierre de la conexión. (Para obtener más información sobre esto, consulte TCP TIME-WAIT en RFC 9293). Si la tasa de solicitudes es alta, es posible que se agote el límite del sistema operativo de puertos disponibles. Para evitar problemas de agotamiento de puertos, se recomienda reutilizar instancias HttpClient para tantas solicitudes HTTP como sea posible.

Para resumir el uso recomendado de HttpClient en términos de administración de la duración, debe usar clientes de larga duración y establecer PooledConnectionLifetime (.NET Core y .NET 5+) o clientes de corta duración creados por IHttpClientFactory.

  • En .NET Core y .NET 5 y versiones posteriores:

    • Use una instancia static o de singleton HttpClient con PooledConnectionLifetime establecido en el intervalo deseado, como 2 minutos, en función de los cambios de DNS esperados. Esto resuelve los problemas tanto de agotamiento de puertos como de cambios de DNS sin sobrecargar IHttpClientFactory. Si necesita poder simular el controlador, puede registrarlo por separado.

    Sugerencia

    Si solo usa un número limitado de instancias HttpClient, también es una estrategia aceptable. Lo que importa es que no se crean y eliminan con cada solicitud, ya que cada una contiene un grupo de conexiones. El uso de más de una instancia es necesario en escenarios con varios servidores proxy o para separar contenedores de cookies sin deshabilitar completamente la administración de cookies.

    • Con IHttpClientFactory, puede tener varios clientes configurados de forma diferente para los distintos casos de uso. Sin embargo, tenga en cuenta que los clientes creados por la fábrica están diseñados para tener una duración corta y que, una vez creado el cliente, la fábrica ya no tiene control sobre él.

      Las instancias de HttpMessageHandler de los grupos de fábrica y, si su duración no ha expirado, pueden reutilizar un controlador del grupo cuando la fábrica crea una nueva instancia de HttpClient. Esta reutilización evita cualquier problema de agotamiento de sockets.

      Si desea la capacidad de configuración que IHttpClientFactory proporciona, se recomienda usar el enfoque cliente con tipo.

  • En .NET Framework, use IHttpClientFactory para administrar sus instancias de HttpClient. Si no usa la fábrica y, en su lugar, crea una nueva instancia de cliente para cada solicitud usted mismo, puede agotar los puertos disponibles.

    Sugerencia

    Si la aplicación requiere cookies, considere la posibilidad de deshabilitar el control automático de cookies o evite IHttpClientFactory. La agrupación de las instancias de HttpMessageHandler da como resultado el uso compartido de objetos CookieContainer. El uso compartido de objetos CookieContainer no previsto suele generar código incorrecto.

Para más información sobre cómo administrar la duración de HttpClient con IHttpClientFactory, consulte las directrices de IHttpClientFactory.

Resistencia con clientes estáticos

Es posible configurar un cliente static o singleton para que utilice cualquier número de canalizaciones de resiliencia utilizando el siguiente patrón:

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

El código anterior:

  • Se basa en el paquete NuGet Microsoft.Extensions.Http.Resilience.
  • Especifica un controlador de errores HTTP transitorio, configurado con la canalización de reintento que, con cada intento, revertirá exponencialmente los intervalos de retraso.
  • Define una duración de conexión agrupada de quince minutos para socketHandler.
  • Pasa socketHandler al resilienceHandler objeto con la lógica de reintento.
  • Crea una instancia de un HttpClient determinado objeto resilienceHandler.

Importante

La biblioteca Microsoft.Extensions.Http.Resilience está marcada actualmente como experimento y puede cambiar en el futuro.

Consulte también