IHttpClientFactory In .NET Core To Avoid Socket Exhaustion

Introduction

In this article, we will see how to use HttpClient and IHttpClientFactory in .Net core applications.

Many times I find that developers use a new HttpClient Object every time they want to call an API. We will see what are the drawbacks of creating a HttpClient object every time and how we can overcome those drawbacks by using IHttpClientFactory.

HttpClient class

Here is the HttpClient class in the framework. You can see it has a disposal method. That means the object will be disposed of after the completion of work.

Http client

Problem

Now think of a scenario where you call an api 10 times using the HTTP client object in the "using" statement or creating a new HttpClient object on each request. So, to demonstrate it, we have a web api application and a console application (client) which we will use to call the Web API, as shown below:

Client console

In the Web API application, we will have a sample API that returns "Temperature information" (default .net core api). We will call this API from the console application in the loop as below under the using statement,

class Program 
{  
    static async Task Main(string[] args) 
    {  
        for (int i = 0; i < 10; i++) 
        {  
            using (var httpClient = new HttpClient()) 
            {  
                var response = await httpClient.GetAsync("https://localhost:44350/weatherforecast");
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(content);  
            }  
        }  
        Console.ReadLine();  
    }  
}  

Note. As we are using the "using" statement, after execution of the "using" code block, the httpClient objects are ready for disposal.

We are making both applications as startup applications. So we can execute api and console client together:

Startup project

Also, this Web API runs on a specific port, you can check it under the properties of the WebApi application and in the Debug section:

Debug

When we run the application, we can see that we are getting a response on the console screen:

Console screen

Everything looks good here, but it is not. The socket connections used to call the APIs are not closed yet.

To check that, open the command prompt and run the command below:

netstat -na | find "44350"

Command prompt

You can see the application has exited, but these socket connections are still open in Time_Wait status. So, the HttpClient object is disposed (as it is under using block and the HttpClient implements IDisposable interface) but the socket connection is not disposed of yet. That may lead to the Socket exhaustion exception.

Though HttpClient implements an IDisposable interface, the recommended approach is not to dispose of it after each external api call. It should be live until your application is live and should be reused for other api calls.

Solution

The recommended solution is IHttpClientFactory. It has a method CreateClient which returns the HttpClient Object. But in reality, HttpClient is just a wrapper, for HttpMessageHandler. HttpClientFactory manages the lifetime of HttpMessageHandelr, which is a HttpClientHandler who does the real work under the hood. There are many other benefits of IHttpClientFactory like Resilient HttpClient using Polly, but that is not in the scope of this article.

As IHttpClientFactory manages the lifetime HttpMesaageHandler objects, So it creates a pool of HttpMessageHanlder objects.

Whenever any client requests a HttpClient Object, it first looks into the HttpMessageHandler object pool, if it finds any object available there, then it returns it instead of creating a new one. If it does not find then it will return a new object.

Here is the code

static async Task Main(string[] args) 
{  
    var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();  
    var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();  
    for (int i = 0; i < 10; i++) 
    {  
        var httpClient = httpClientFactory.CreateClient();  
        var response = await httpClient.GetAsync("https://localhost:44350/weatherforecast");
        var content = await response.Content.ReadAsStringAsync();
        Console.WriteLine(content);  
        Console.WriteLine(Environment.NewLine);  
    }  
    Console.ReadLine();  
}  

Here we are getting the HttpClient object from Dependency Injection. In the for loop we are calling the createClient() method of HttpClientFactory to get the instance of the HTTP client (From pool if exists, if not then a new object).

Though we are calling CreateClient inside the loop, when we run the application you can see, that we have a single socket connection with the status "Established".

Established

Note. To use IHttpClientFatory, you need to add the "Microsoft.Extensions.Http" nuget package:

Package

Other Solution (Not Preferred)

The other option that works per request is to have a global HttpClient object which developers generally create like below.

class Program {  
    static async Task Main(string[] args) {  
        HttpClient httpClient = new HttpClient();  
        for (int i = 0; i < 10; i++) {  
            var response = await httpClient.GetAsync("https://localhost:44350/weatherforecast").Result.Content.ReadAsStringAsync();  
            Console.WriteLine(response);  
            Console.WriteLine(Environment.NewLine);  
        }  
        Console.ReadLine();  
    }  
}  

As the HttpClient object is outside the for loop, it will also reuse the same HttpClient object that we declared before the loop, but still, we will not get the liberty of IHttpClienFatory like managing the lifetime of httpClient and pooling objects for reuse. So, I would not recommend this approach.

Conclusion

We should be careful while using HttpClient. It is quite a heavy resource-oriented process to create an object of HttpClient. That is why IHttpClientFactory comes into the picture to provide you with a pool of HttpClient objects, and that is why it is faster.


Similar Articles