Aug 08

Prior to .NET Core 2.1 anyone who has tried to use HttpClient at scale (maybe from a microservice) may have run into the dreaded socket exhaustion issue.

Socket exhaustion happens because HttpClient is designed to be instantiated once and re-used throughout the life of an application, if you make HTTP calls in a server application for every request you will run out of sockets (because sockets get left in TIME_WAIT state for 240 seconds by default).

Using IHttpClientFactory can improve performance by 43%!

A simple fix would be to instantiate HttpClient as a singleton, however you might get an issue getting DNS updates which is a real problem if you are load balancing or have cloud managed services.

Knowing this limitation and realising that HttpClient’s are being used more from servers because of microservices, the .NET Core team have come up with IHttpClientFactory.

A implementation of IHttpClientFactory makes use of connection pooling, and HttpClient’s are disposed of after a specified time. This resolves the socket exhaustion issue as it controls how many sockets get left in the TIME_WAIT state, and because the connections do get disposed we get DNS updates when a new HttpClient is created in the pool.

If you want to learn more about IHttpClientFactory read @stevejgordon excellent blog post.

How does RestClient implement IHttpClientFactory?

Now you know a bit about IHttpClientFactory, how does RestClient implement IHttpClientFactory? Much the same HttpClient, but with all the flexibility and power of RestClient.

Here’s how to use it:

To use IHttpClientFactory with RestClient first register your RestClient as a service in Startup.cs. This is the simplest version if you only need one RestClient.

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
   {
      services.AddRestClient("https://api.github.com");
   }
}

Then inject IRestClientFactory into your controller, which is how we support IHttpClientFactory.

public class GitHubController : Controller
{
   private readonly IRestClientFactory _restClientFactory;
        
   public GitHubController(IRestClientFactory restClientFactory)
   {
      _restClientFactory = restClientFactory;
   }

   [Route("github/users/dalsoft"), HttpGet]
   public async Task> CreateClient()
   {
      dynamic restClient = _restClientFactory.CreateClient();
            
      var repositories = await restClient.users.dalsoft.repos.Get();
            
      return repositories;
   }        
}

Multiple RestClient’s using IRestClientFactory

If you need to support multiple RestClient’s use a named client.

First register your named RestClient as a service in Startup.cs

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
   {
      services.AddRestClient("MyNamedGitHubClient", "https://api.github.com/orgs/");
   }
}

Then inject IRestClientFactory into your controller (which is how we support IHttpClientFactory), and call CreateClient passing the name of your client.

public class GitHubController : Controller
{
   private readonly IRestClientFactory _restClientFactory;
        
   public GitHubController(IRestClientFactory restClientFactory)
   {
      _restClientFactory = restClientFactory;
   }

   [Route("github/orgs/dotnet/repos"), HttpGet]
   public async Task> CreateClient()
   {
      dynamic restClient = _restClientFactory.CreateClient("MyNamedGitHubClient"); //Get Client by name
      
      var repositories = await restClient.dotnet.repos.Get();
            
      return repositories;
   }        
}

For everything you need know about RestClient visit the RestClient site https://restclient.dalsoft.io

Leave a Reply

preload preload preload