HttpClient for High-throughput Applications

It’s been a while since I’ve posted anything on the MobTowers blog. In the meantime, Mrs MobTowers and I have created a small human alarm clock. Lack of sleep makes me a bear of very little brain.

So, I’ve been busy! But I thought I’d jump straight into something tricky, and clear up some confusing points around the HttpClient, with particular focus on making it work well in high-throughput scenarios.

The HttpClient should be, for the most part, very simple to use. Here’s an example of a simple GET request.

using (var client = new HttpClient())
    client.BaseAddress = new Uri("");

    HttpResponseMessage result = await client.GetAsync("posts/1");

The above snippet makes a simple GET request to And to be clear, there’s nothing wrong with this code.

You’ll notice, we’re disposing of the HttpClient instance by wrapping its creation in a using block. We’re taught to dispose of any disposable object when we’re finished with it. And, 99.9% of the time, we should. For the majority of applications, you should continue to dispose your HttpClient instances, as above.


In high-throughput applications, don’t dispose of your HttpClient instance. Reuse it!

“But it’s disposable!” I hear you shout. You’re right to shout. The class implements IDisposable, so you’d expect that it knows how to properly dispose of any resources it reserves. It’s not an unreasonable assumption that you should be able to instantiate it in a using block. Well, whenever you make an HTTP request, an outgoing TCP port is reserved. When you dispose of the HttpClient, it closes the connection to the remote server, but doesn’t relinquish the ports for other clients to use. By default, it actually takes 4 minutes after each request for the associated port to become available again. Since there’s only a limited number of available ports on any machine (ports 49152-65535), you can very quickly run out. If this happens, you’ll start getting port exhaustion errors during periods of high traffic.

So, how should we create and manage HttpClient instances for high-throughput? If we shouldn’t dispose of the client, how do we re-use it? Well, the HttpClient is designed with this in mind. All instance methods are fully thread-safe, so it’s possible to maintain a single HttpClient within an application. The client natively manages its ports for re-use, so it’s much harder to run out.

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.


Option 1 – Static Field

It’s possible to create a single static HttpClient and reuse it everywhere in your application. This is Microsoft’s recommended approach.

public class GoodController : ApiController
    private static readonly HttpClient HttpClient;

    static GoodController()
        HttpClient = new HttpClient();

You could even make the field public, or expose it as a property, and then share it everywhere in your application.

Option 2 – Dependency Injection

If you’re using dependency injection, a nice way to handle this is to inject the HttpClient anywhere you want to use it. You could inject it as a singleton if your framework supports it. Here’s an example of how to register a client using Autofac.


Or with dotnet core.


You can then use constructor injection to get a reference to your shared instance.

public class GoodController : ApiController
    private readonly HttpClient _httpClient;

    public GoodController(HttpClient httpClient)
        _httpClient = httpClient;

Important Notes

Re-using the HttpClient raises some interesting issues that you won’t have seen making single requests.

Instance Members

Not all members of the HttpClient class are guaranteed to be thread-safe. Furthermore, these properties will throw exceptions if you try to change them after the first request has been made. Properties such as BaseAddress, and DefaultRequestHeaders should not be modified after instance creation. Therefore, if you need any information to differ on a per-request basis, such as authentication headers, you should set them on the individual request, rather than on the client.

var request = new HttpRequestMessage(HttpMethod.Post, "")
    Content = new StringContent(
            Title = "foo",
            UserId = "1",
            Body = "bar"

request.Headers.Authorization = new AuthenticationHeaderValue("basic", "dskjecbopdsfljhqddfcxjswj");

var response = await _httpClient.SendAsync(request);

Request Retries

If a request fails, and you attempt to resend it, you may get the following exception.

System.InvalidOperationException: The request message was already sent. Cannot send the same request message multiple times.

The HttpClient will not send the same request twice. There are fancy solutions to this, such as cloning the request, but by far the most reliable way to retry a request is to create a new one. If you have a method which builds a request and sends it, that is what you want to retry, not just the sending part.

DNS Changes

When reusing your client, as discussed, it will maintain a pool of outgoing TCP connections. This means it will essentially hold on to various ports for each endpoint, and re-use them. So what happens if the DNS for an endpoint changes whilst you have open connections? Well, nothing happens. DNS resolution happens when initiating a connection, and is not performed again. To fix this, connections need to be allowed to expire within the client, which doesn’t happen by default. You can set the timeout for a connection to a specific endpoint using the ServicePointManager, which is a static helper which controls many aspects of TCP connections. This will also add a small overhead to connections which need to be re-established, so only use this if you experience issues. You’ll need to set this property for each separate endpoint used within your app.

// Note: this is only available in the .NET Framework.
ServicePointManager.FindServicePoint(new Uri("")).ConnectionLeaseTimeout = 6000; // 1 minute

Any other tips for using the HttpClient? Please leave a comment below.

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *