DEV Community

Cover image for Introducing the HttpClientFactory
Xhanti Mda
Xhanti Mda

Posted on

Introducing the HttpClientFactory

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.

TIME_WAIT

An example of connections in the TIME_WAIT state.

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.

AddHttpClient

An example of the direct usage of the HttpClientFactory.

InjectClient

An example of injecting HttpClientFactory.

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.

ConfigureNamedClient

An example of configuring a named HttpClient.
  • To access a named HttpClient, you just have to pass the name of the configured client to the CreateClient method on IHttpClientFactory.

ConsumeNamedClient

An example of consuming a named HttpClient.

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.

CreateTypedClient

An example of creating a typed HttpClient.

ConfigureTypedClient

An example of configuring a typed HttpClient.

ConsumingTypedClient

An example of consuming a typed HttpClient.

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.

ConsumingFromServices

An example of consuming the FromServices attribute.

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)