Singleton Httpclient VS Creating New Httpclient Request

Singleton httpclient vs creating new httpclient request

Update: It seems that using a single static instance of HttpClient doesn't respect DNS changes, so the solution is to use HttpClientFactory. See here for Microsoft docs about it.

To use the HttpClientFactory you have to use Microsoft's dependency injection. This is the default for ASP.NET Core projects, but for others you will have to reference Microsoft.Extensions.Http and Microsoft.Extensions.DependencyInjection.

Then when you're creating your service container, you simply call AddHttpClient():

var services = new ServiceCollection();
services.AddHttpClient()
var serviceProvider = services.BuildServiceProvider();

And then you can inject IHttpClientFactory into your services, and behind the scenes HttpClientFactory will maintain a pool of HttpClientHandler objects - keeping your DNS fresh and preventing problems with connection pool exhaustion.


Old answer:

Singleton is the correct way to use HttpClient. Please see this article for full details.

Microsoft docs state:

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors. Below is an example using HttpClient correctly.

And indeed, we found this in our application. We have code that can potentially make hundreds of API requests in a foreach loop, and for each iteration we were creating an HttpClient wrapped in a using. We soon started getting red herring errors from our MongoClient saying that it had timed out trying to connect to the database. After reading the linked article, we found that even after disposing of HttpClient, and realised that we were exhausting the available sockets.

The only thing to note is that things like DefaultRequestHeaders and BaseAddress will be applied anywhere that HttpClient is used. As a singleton, this is potentially throughout the application. You can still create multiple HttpClient instances in your application, but just be aware that each time you do, they create a new connection pool and, as such, should be created sparingly.

As pointed out by hvaughan3, you also can't change the instance of HttpMessageHandler used by the HttpClient, so if this matters to you, you would need to use a separate instance with that handler.

HttpClient inject into Singleton

  1. Will it use HttpClientFactory to create an instance of HttpClient?

Yes. A default HttpClient is registered as a transient service during HttpClientFactory registration.


  1. I guess it uses HttpClientFactory but will it have issues with DNS changes in that case?

Correct, it still would. As you inject it into a singleton, HttpClient here will be created only once. In order to make use of HttpClientFactory's HttpMessageHandler pooling, you'd need your HttpClients to be short-lived. So, for this you would rather need to inject IHttpClientFactory itself and call CreateClient when you need one. (Note that short-living HttpClients only apply to HttpClientFactory usage). BTW switching to a typed client will not help when injecting into a singleton, HttpClient will still end up being created only once, see https://github.com/dotnet/runtime/issues/64034.

Also, you can actually avoid HttpClientFactory entirely and still have DNS changes respected. For that you may have a static/singleton HttpClient with PooledConnectionLifetime set to some reasonable timeout (e.g. the same 2 minutes HttpClientFactory does)

services.AddSingleton(() => new HttpClient(
new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(2) }
));

Is a Singleton HttpClient receiving a new HttpMessageHandler After X Minutes

does the internal HttpMessageHandler expires also when the wrapping HttpHandler is used as a Singleton?

WARNING: Keeping HttpClient instances alive through Singletons is not safe.

The configured HttpMessageHandler instance will expire after two minutes. Any HttpClient that is created after the the expiry date will get a new HttpMessageHandler containing a new expiry date. But this doesn't help a HttpClient instance that is kept alive.

IMPORTANT: HttpClient will keep using the same HttpMessageHandler for as long as it lives and will, therefore,
not respect DNS changes. Only new HttpMessageHandler instances will see DNS updates.

This means that as long as you keep the HttpClient alive, that HttpClient will miss DNS changes, which is why HttpClient instances should only live for a short period of time. Creating and cleaning up HttpClient instances is very cheap as long as you reuse the underlying HttpMessageHandler instances (which is what the infrastructure does for you).

In your case, unfortunately, your HttpClient is injected into the CatalogService, which is injected into a Singleton consumer. The Singleton keeps the CatalogService alive and, therefore, indirectly keeps the HttpClient alive for the duration of the application—Your HttpClient is now a Captive Dependency.

What you're experiencing is an unfortunate design flaw in the new .NET Core IHttpClientFactory infrastructure. I consider this a flaw because IMO the infrastructure should prevent you from holding HttpClient instances captive, e.g. by registering the client (your CatalogService) as Scoped. I reported this issue back in January 2019. Microsoft has acknowledged the problem but has, as of this writing, no solution available.

As there is no solution yet, you have to be very careful not to cause HttpClient to be kept alive as Captive Dependency. This means you can't inject it into Singleton consumers (even indirect one's).

A good way to prevent this is by registering the client (your CatalogService) as Scoped. This allows the framework's configuration system to validate whether it is injected into a Singleton. But since there is no direct support for this, you will have to make this registration manually, by using the following extension method for instance:

public static IHttpClientBuilder AddTypedClientScoped<TClient>(
this IHttpClientBuilder builder)
where TClient : class
{
...
builder.Services.AddScoped<TClient>(s =>
{
var httpClientFactory = s.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient(builder.Name);
var factory = s.GetRequiredService<ITypedHttpClientFactory<TClient>>();
return factory.CreateClient(httpClient);
});

return builder;
}

While starting an ASP.NET Core website while debugging, the framework will validate scopes for you, which causes an exception when the client is injected into a singleton.

But please be warned that the use this extension method doesn't detect all captive HttpClient instances. That's because there are places in ASP.NET Core where components are registered as Transient will still being kept alive for the duration of the application. One such case is hosted services. The AddHostedService extension method is such example. Hosted services are registered as Transient while being kept alive as Singleton. In my optinion another design flaw. But that means that you should be careful while directly or indirectly injecting HttpClient instances into hosted services.

What is the overhead of creating a new HttpClient per call in a WebAPI client?

HttpClient has been designed to be re-used for multiple calls. Even across multiple threads.
The HttpClientHandler has Credentials and Cookies that are intended to be re-used across calls. Having a new HttpClient instance requires re-setting up all of that stuff.
Also, the DefaultRequestHeaders property contains properties that are intended for multiple calls. Having to reset those values on each request defeats the point.

Another major benefit of HttpClient is the ability to add HttpMessageHandlers into the request/response pipeline to apply cross cutting concerns. These could be for logging, auditing, throttling, redirect handling, offline handling, capturing metrics. All sorts of different things. If a new HttpClient is created on each request, then all of these message handlers need to be setup on each request and somehow any application level state that is shared between requests for these handlers also needs to be provided.

The more you use the features of HttpClient, the more you will see that reusing an existing instance makes sense.

However, the biggest issue, in my opinion is that when a HttpClient class is disposed, it disposes HttpClientHandler, which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager. This means that each request with a new HttpClient requires re-establishing a new TCP/IP connection.

From my tests, using plain HTTP on a LAN, the performance hit is fairly negligible. I suspect this is because there is an underlying TCP keepalive that is holding the connection open even when HttpClientHandler tries to close it.

On requests that go over the internet, I have seen a different story. I have seen a 40% performance hit due to having to re-open the request every time.

I suspect the hit on a HTTPS connection would be even worse.

My advice is to keep an instance of HttpClient for the lifetime of your application for each distinct API that you connect to.

Which type of singleton pattern should be used for creating HTTP client for my web application

This doesn't sound like a good idea. In particular, take a peek into the docs of the HttpClient class:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx

This means that accessing the very same singleton instance from multiple threads will lead to undefined issues.

What you could do however, is you could reuse the same instance across a single request. This can be done by storing an instance in the Items container:

   private static string ITEMSKEY = "____hclient";

public static HttpClient GetClient()
{
if ( HttpContext.Current.Items[ITEMSKEY] == null )
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(DemoConstants.DemoAPI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

HttpContext.Current.Items.Add( ITEMSKEY, client );
}

return (HttpClient)HttpContext.Current.Items[ITEMSKEY];
}

Note, that since the HttpClient implements IDisposable, it still could be a good idea to dispose such instance somewhere in the pipeline, for example in the EndRequest event of the application pipeline.

Update: as noted in a comment by @LukeH, the updated version of the docs for the .NET 4.5 and 4.6 states that some of methods of the HttpClient class are thread safe:

https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.110%29.aspx

The updated remarks section states that a single instance is basically a collection of shared settings applied to all requests executed by this instance. Then, the docs says:

In addition, every HttpClient instance uses its own connection pool, isolating its requests from requests executed by other HttpClient instances.

This means that the isolation of different pools could still make sense, my personal recommendation would still be then to not to have a singleton, as you possibly would still need to change some settings between consecutive requests.

HttpClient Singleton Implementation in ASP.NET MVC

Do you really want one instance?

I don't think you want one instance application-wide. You want one instance per thread. Otherwise you won't get very good performance! Also, this will resolve your questions #3 and #4, since no two threads will be accessing the same HttpClient at the same time.

You don't need a singleton

Just use Container.Resolve with the PerThreadLifetimeManager.



Related Topics



Leave a reply



Submit