A few years ago, I came across a blog post by the ASP.NET Monsters which was titled : YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE. This post shared a trick that I've used numerous times to avoid socket exhaustion by not disposing of the HttpClient.
To dispose, or not to dispose, that is the question.
Creating a HttpClient instance, internally creates a HttpMessageHandler chain. It's the handler chain that will be responsible for establishing a connection.
So when we dispose of the HttpClient, we actually dispose of the HttpMessageHandler which will eventually close the connection.
The risk of this is that the connection doesn't get closed as soon as the client is disposed of.
When we actively close a connection, the port is not immediately released, it goes into a TIME_WAIT state.
If this continues to happen, it will lead to socket exhaustion.
A solution to the above issue was to have a single instance of a HttpClient that would be shared across the application.
The problem with the above solution is that connections would be kept open and reused forever and any DNS changes made on the remote services wouldn't be honoured.
This would lead to applications pointing to offline server instances or incorrect environments.
Introducing the HttpClientFactory
- The HttpClientFactory was introduced with .NETCore 2.1.
- The HttpClientFactory gives us a central place to configure and create HttpClients.
- It manages the lifetime of the underlying HttpMessageHandler chains. It does this by managing a pool of these MessageHandlers and keeping them open until they expire.
- The MessageHandlers are kept alive for a maximum of 2 minutes (by default).
- This gives us the best of both, holding on to connections long enough while still honouring DNS changes.
Patterns of consuming the HttpClientFactory
Direct Usage
- Simple usage of the HttpClientFactory requires minimal code changes in your application.
- Install the Microsoft.Extensions.Http nuget package if your application does not reference the Microsoft.AspNetCore.App metapackage.
- Just by calling the AddHttpClient extension method on the IServiceCollection gives you the goodies of instance management out of the box.
- It allows you to inject the HttpClientFactory anywhere in your application, which you can use to create HttpClient instances with.
Named Clients
- Named clients gives us the ability to create logical configurations for different services that our application communicates with.
- Configuring a named client is as simple as just calling the AddHttpClient method, passing in the name of the client and a delegate which takes in a HttpClient.
- To access a named HttpClient, you just have to pass the name of the configured client to the CreateClient method on IHttpClientFactory.
Typed Clients
- Typed clients are the recommended (by Microsoft) way of consuming HttpClient.
- A typed client is just a class that accepts a HttpClient as a constructor parameter.
- Typed clients give us the benefit of encapsulating service-specific code into a single class.
Bonus
You may have noticed the FromServices Attribute used in the GetMorty action and asked yourself WTF!
- The FromServices attribute gets the action parameter from the container.
- This happens during the ModelBinding stage in the MVC request lifecycle after the ControllerActionInvoker finds the matching action in the controller, based on the request.
- An instance of the type is bound to the parameter of the action just before it gets invoked.
Final words
- Use the HttpClientFactory for more resilient HTTP calls.
- You can override the default HttpMessageHandler lifetime by chaining the SetLifeTime method to the AddClient method when configuring HttpClient.
- As recommended, use Typed Clients were possible.
- Access sample source code here
Top comments (0)