IO Performance: Reuse HttpClient to Avoid Connection Overhead

HttpClient has been around since .NET Framework 4.5, but the way it should be used has changed over time. In modern .NET, HttpClient is the preferred API for making HTTP calls, and starting with .NET Core 2.1, it uses SocketsHttpHandler as the underlying implementation.

One common pattern I still see developers use looks like this:

using var client = new HttpClient();
return await client.GetStringAsync(url);

At first glance, this looks fine. The code is short, readable, and properly disposes of the object. Unfortunately, it can be very expensive when used repeatedly.

Each HttpClient instance owns its own connection pool. When the instance is disposed, the underlying connections are also disposed. The next request has to rebuild that work again, which can mean creating new TCP connections, performing DNS lookups, negotiating TLS, and rebuilding the handler pipeline.

A much better approach is to reuse the same HttpClient instance. For simple scenarios, that can be done with a static field:

private static readonly HttpClient _httpClient = new();

Then use the shared instance instead of creating a new one for every request:

return await _httpClient.GetStringAsync(url);

Performance Breakdown

In this benchmark, reusing HttpClient from a static field increased performance by 12,109x and reduced allocations by 10x.

That is not a small win. That is the kind of performance improvement that can remove unnecessary overhead from every outbound HTTP call in an application.

This is faster because the shared HttpClient can reuse the underlying connection pool. This reduces repeated TCP connection creation, DNS lookups, TLS handshakes, and handler pipeline setup.

The key rule is simple:
Do not create and dispose of a new HttpClient for every request.

Use a long-lived HttpClient, or use IHttpClientFactory when your application needs dependency injection, named clients, typed clients, logging, resiliency policies, or multiple differently configured HTTP clients.

I hope the .NET team adds a new analysis setting to catch this issue in code.

Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks

If you liked this article, please buy David a cup of Coffee by going here: https://www.buymeacoffee.com/dotnetdave

© The information in this article is copywritten and cannot be reproduced in any way without express permission from David McCarter.


Discover more from dotNetTips.com

Subscribe to get the latest posts sent to your email.

Leave a Reply