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 new HttpClient Object every time they want to call an API. We will see what are the drawbacks of creating HttpClient object every time and how we can overcome that drawback by using IHttpClientFactory.
 

HttpClient class

 
Here is the HttpClient class in framework. You can see it has a dispose method. That means the object will get disposed after completion of work.
 
 
Problem
 
Now think of a scenario where you call an api 10 times using httpclient object in "using" statement or creating 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:
 

In the Web API application, we will have a sample API which returns "Temperature information" (default .net core api). We will call this API from console application in loop as below under using statement, 
  1. class Program {  
  2.     static async Task Main(string[] args) {  
  3.         for (int i = 0; i < 10; i++) {  
  4.             using(var httpClient = new HttpClient()) {  
  5.                 var response = await httpClient.GetAsync("https://localhost:44350/weatherforecast").Result.Content.ReadAsStringAsync();  
  6.                 Console.WriteLine(response);  
  7.             }  
  8.         }  
  9.         Console.ReadLine();  
  10.     }  
  11.   }  
  12. }  
Note
As we are using the "using" statement, after execution of "using" code block, the httpClient objects are ready for disposal.  
 
We are making both applications as startup application. So we can execute api and console client together:
 
 
Also, this Web API runs on a specific port, you can check it under the properties of WebApi application and in the Debug section:
 
 
When we run the application, we can see that we are getting a response on the 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"
 
 
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 yet. That may lead to the Socket exhaustion exception.
 
Though HttpClient implements IDisposable interface, but the recommended approach is not to dispose 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 actually 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 scope of this article.
 
As IHttpClientFactory manages the lifetime HttpMesaageHandler objects, So it creates a pool of HttpMessageHanlder objects.
Whenever any client request 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,
  1. static async Task Main(string[] args) {  
  2.     var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();  
  3.     var httpClientFactory = serviceProvider.GetService < IHttpClientFactory > ();  
  4.     for (int i = 0; i < 10; i++) {  
  5.         var httpClient = httpClientFactory.CreateClient();  
  6.         var response = await httpClient.GetAsync("https://localhost:44350/weatherforecast").Result.Content.ReadAsStringAsync();  
  7.         Console.WriteLine(response);  
  8.         Console.WriteLine(Environment.NewLine);  
  9.     }  
  10.     Console.ReadLine();  
  11. }  
Here we are getting the HttpClient object from Dependency Injection. In the for loop we are calling createClient() method of HttpClientFactory to get the instance of http client (From pool if exists, if not then a new object).
 
Though we are calling CreateClient inside loop, when we run the application you can see, we have a single socket connection with status as "Established".
 
 
Note
To use IHttpClientFatory, you need to add "Microsoft.Extensions.Http" nuget package:
 
 
Other Solution (Not Preferred)
 
The other option which works per request is to have a global HttpClient objects which developers generally create like below,  
  1. class Program {  
  2.     static async Task Main(string[] args) {  
  3.         HttpClient httpClient = new HttpClient();  
  4.         for (int i = 0; i < 10; i++) {  
  5.             var response = await httpClient.GetAsync("https://localhost:44350/weatherforecast").Result.Content.ReadAsStringAsync();  
  6.             Console.WriteLine(response);  
  7.             Console.WriteLine(Environment.NewLine);  
  8.         }  
  9.         Console.ReadLine();  
  10.     }  
  11. }  
As the HttpClient object is outside the for loop, it will also reuse the same HttpClient object which we declared before loop, but still we will not get the liberty of IHttpClienFatory like managing 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 a pool of HttpClient objects, and that is why it is faster.