Using Dotnet HttpClient Correctly or live with sockets exhaustion
Situation
Is common in dotnet developers to use HttpClient (a disposable object) within a “using” statement.
This use of HttpClient open a new socket each time the using statement is execute, because when the statement ends the HttpClient instance is disposed.
This results in the continuous opening and closing of ports on the host.
But the rule of best practices of the classes that inherit from IDisposable says:
“As a rule, when you use an IDisposable object, you should declare and instantiate it in a using
statement.”.
In the case of HttpClient is not the best practice
If you do a request in a loop for 10 times and use a HttpClient in a using statement you can see a new connection
per each instance of HttpClient request if you check the connections on your host with the state: TIME_WAIT
Why TIME_WAIT? This is because TCP (TCP
STATES) has configured a waiting timeout for wait for a new request, because assume that a new request
will come. If you do this often, the sockets could get exhausted.
Solution
So, How could fix it?
Well, the first approach is to create a HttpClient per earch base Uri to interact.
For example, if I have an application that need to interact with Google Drive API and Dropbox API, I will create
two HttpClient objects, one for GD API and the other for Dropbox API. Maybe I could create a class with the two
HttpClient or two classes each one with a HttpClient. Both could be static and when I create the object I would
initialized the HttpClient with the correct configuration. When I need to do a request, just send the request
waiting for the response and then not close the HttpClient.
The HttpClient object will live from the initialization until the end of the application and it will always be
the same as the one used to connect. With this we only keep an open socket and it’s the same one that we will
use to make all the requests. Of course, separate it for each different base Uri since if it changes, you are
instantiating a new socket.
The second approach is to use the HttpClientFactory released from Dotnet core
2.1.
This class is created to centraliced the control of the HttpClients.
You can use this class in the follow ways:
-Registering each use of HttpClient as a client service as a middleware
-Registering each use of HttpClient as a client service as a middleware with naming
-Registering each use of HttpClient as a class/interface (typed) (to use with DI)
-With HttpHandlers
-WIth Polly based handlers
-Managing HttpClient as a factory (just creation of HttpClient as be necessary)
The HttpClientFactory resolves any internally problem related to sockets exhaustion and DNS problems.