Vaughan

Why you should use the IHttpClientFactory in ASP.NET Core

.NET Core

One surprising issue when using the HttpClient, is the issue of port exhaustion. This often happens when an application creates a lot of HttpClient instances for short requests. When there is sufficient load the machine may run out of tcp ports and then it won\'t be able to make another request until a port opens up. The reason for this is the fact that disposing a HttpClient will still keep a tcp connection open in TIME_WAIT status. This then relies on the system configured timeout which is often a couple of minutes. Its the worst kind of bug because it often happens in production when you are under heavy load.

The general advice has always been to pool your HttpClient instances and use the same instance for many related requests. Before .net core people generally created their own implementation of pretty much the same http client factory class.

With all this in mind, .net now has the IHttpClientFactory service in .NET Core projects. This service allows you to register a named or typed HttpClient for specific services or use cases. It manages the pooling and lifetime of HttpClientMessageHandler instances that do the heavy lifting of the HttpClient class.

The two main ways you would do this is to either use a named client otherwise you could use a typed client.

An example of the named client is if you register the following in the ConfigureServices method:


services.AddHttpClient(phonebook, c =>
    {
c.BaseAddress = new Uri("https://phonebook.com/");
        c.DefaultRequestHeaders.Add("Accept", "application/json");
    });

You can then inject it into your class with the IHttpClientFactory service. For example.


public class PhonebookService 
{
    private readonly IHttpClientFactory httpClientFactory;

    public PhonebookService(IHttpClientFactory httpClientFactory)
    {
        this.httpClientFactory = httpClientFactory;
    }

    public async Task<int> GetNumber(string name)
    {

        var request  = new HttpRequestMessage(HttpMethod.Get, $"username/{name}");

        var client = httpClientFactory.CreateClient("phonebook");

        var response = await client.SendAsync(request);

        response.EnsureSuccessStatusCode();

        var phoneNumber = await response.Content.ReadAsStringAsync();

        return int.Parse(phoneNumber);
    }
}

The other way is to use a typed client. One nice thing about this is that you don\'t need to use a string name. It can be a problem if the scope is a singleton though since you are effectively keeping the same HttpClient instance for the lifetime of the application (I know its the advice but long running state can cause its own issues).

You add it in the ConfigureServices as:

services.AddHttpClient<PhonebookService>();

Then the client directly gets injected into your contructor as:

public class PhonebookService 
{
    HttpClient client;
    public PhonebookService(HttpClient client)
    {
        this.client = client;
    }

    public async Task<int> GetNumber(string name)
    {
        var request  = new HttpRequestMessage(HttpMethod.Get, $"username/{name}");

        var response = await client.SendAsync(request);

        response.EnsureSuccessStatusCode();

        var phoneNumber = await response.Content.ReadAsStringAsync();

        return int.Parse(phoneNumber);
    }
}

Related Posts

BMC logoBuy me a coffee