On a recent piece of work, I was updating some code in our codebase which makes use of a HttpClient for fairly similar actions, repeatedly. During a code review, someone noted that I was repeatedly creating a new HttpClient instance for every time we were hitting a certain HTTP endpoint. He noted that HttpClient is a long-lived resource, and we really shouldn’t be bringing up and tearing down new instances as it can exhaust the pool of TCP connections for the application.

A reduction strategy that I used for our code, since my class is a singleton that is always acquired via dependency injection, is to create a single instance of HttpClient in the constructor that is shared throughout the life of the class, and implement the IDisposable interface on the class. In the Dispose() method, simply call the disposal method of the HttpClient.

Here’s an example:

[AutoRegister(RegistrationType=RegistrationType.SingleInstance)]
public class FooBarClass : IFooBarClass, IDiposable {
    private HttpClient client;


    public FooBarClass() {
        client = new HttpClient();
        // Any common initialisation on the client here
    }


    public async HttpResponse Example() {
        return await client.GetAsync("createsend.com");
    }


    public void Dispose() {
        httpClient?.Dispose();
    }
}

This is in the interim, until I’ve found a better cross-application story for dealing with HttpClient resource sharing. In particular, I don’t really understand how to manage per-dependency mutations of a single HttpClient instance.

Something to note when using a long-life HttpClient instance, which someone on my team pointed out, is that DNS changes aren’t honoured across the lifetime of the object. So if you’re calling a service that makes use of blue-green deployments, failovers, or something like Heroku to balance across web workers, you might be hitting a silently faulty endpoint. This didn’t matter in our example, because the dependent service uses a raw IP address delivered via Consul service discovery, but this isn’t always the case. If it matters to you, this linked article provides an explanation of how it works and a neat solution.