Optimize HttpClient Usage in .NET Core

.Net Core

Part 2 LinkOptimize HttpClient Usage in .NET Core Part 2 (c-sharpcorner.com)

HttpClient class

The HttpClient class in System.Net.Http facilitates the sending of HTTP requests and retrieval of responses from resources identified by URIs. Each HttpClient instance encapsulates a set of settings applied universally to all requests it executes, with its own connection pool ensuring the isolation of requests. Beginning with .NET Core 2.1, the SocketsHttpHandler class offers the underlying implementation, ensuring consistent behavior across diverse platforms.

There are two options for implementing HttpClient in an app and it's recommended to choose the implementation based on the client's lifetime management needs.

  • For long-lived clients: Create a static or singleton instance using the HttpClient class and set PooledConnectionLifetime
  • For short-lived clients: Use clients created by IHttpClientFactory.

Long-Lived Client

HttpClient only resolves DNS entries when a connection is created. It doesn't track time to live (TTL) durations specified by the DNS server. If DNS entries change regularly, the client won't be aware of those updates. To solve this issue, you can limit the lifetime of the connection by setting the PooledConnectionLifetime property so that the DNS lookup is repeated when the connection is replaced.

In the following example, HttpClient is configured to reuse connections for 5 minutes. After the TimeSpan specified by PooledConnectionLifetime has elapsed, the connection is closed, and a new one is created.

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

Pooled connections

The HttpClient's connection pool is tied to its underlying SocketsHttpHandler. The HttpClient also disposes of all existing connections in the pool. Consequently, subsequent requests to the same server necessitate new connection creation, incurring a performance penalty due to unnecessary connection overhead. Additionally, TCP ports aren't immediately released upon closure, potentially leading to port exhaustion, especially under high request rates. To mitigate port exhaustion issues, it's advisable to reuse HttpClient instances for multiple HTTP requests whenever feasible.

Short-Lived Client

The IHttpClientFactory acts as a factory abstraction enabling the creation of HttpClient instances with tailored configurations, introduced in .NET Core 2.1. It simplifies the integration of third-party middleware for common HTTP-based .NET workloads. By utilizing the AddHttpClient extension methods, the IHttpClientFactory and associated services are seamlessly added to the IServiceCollection.

IHttpClientFactory Implementations

Package Reference: https://www.nuget.org/packages/microsoft.extensions.http

With modern application development principles driving best practices, the IHttpClientFactory serve is a factory abstraction that can create HttpClientinstances with custom configurations.IHttpClientFactory was introduced in .NET Core 2.1. Common HTTP-based .NET workloads can take advantage of resilient and transient-fault-handling third-party middleware with ease.

In Startup DI

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);  

builder.Services.AddHttpClient(); 

Usage

using HttpClient client = httpClientFactory.CreateClient(); 

Consumption patterns

There are several ways iHttpClientFactory can be used in an app.

  • Basic usage
  • Named clients
  • Typed clients
  • Generated clients

We will see two patterns here:

Basic usage

Adding HttpClientFactory registration is accomplished by invoking AddHttpClient.

In Startup DI

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);  

builder.Services.AddHttpClient(); 

Usage

using HttpClient client = httpClientFactory.CreateClient(); 

Named clients

Named clients are advantageous when an application necessitates numerous unique implementations of HttpClient, with each HttpClient instance requiring different configurations. These configurations for a named HttpClient can be specified during registration on the IServiceCollection.

builder.Services.AddHttpClient("ActionHttpClientName",  

client => {  

 client.BaseAddress = new Uri("https://api.sample.com/");  

 client.DefaultRequestHeaders.Add("apikey", "LOKU#(SHHHH773");  

}); 

In the above code, the client is configured with

  • A name that's "ActionHttpClientName".
  • The base addresshttps://api.sample.com/
  • Added the apiKey for authentication purposes.

Usage

using HttpClient client = _httpClientFactory.CreateClient("ActionHttpClientName"); 

HttpClientlifetime management

A new HttpClient instance is returned each timeCreateClientis called on theIHttpClientFactory. One HttpClientHandler instance is created per client name. The factory manages the lifetimes of theHttpClientHandlerinstances. IHttpClientFactorycaches theHttpClientHandlerinstances created by the factory to reduce resource consumption. AnHttpClientHandlerinstance may be reused from the cache when creating a newHttpClientinstance if its lifetime hasn't expired. Caching handlers is desirable as each handler typically manages its own underlying HTTP connection pool. Creating more handlers than necessary can result in socket exhaustion and connection delays. Some handlers also keep connections open indefinitely, which can prevent the handler from reacting to DNS changes.

Reference