The correct way to open HttpClient in.net Core

Time:2019-10-23

preface

In the era of Asp.Net Core 1.0, HttpClient brought endless troubles to developers due to design problems, using Asp.Net Core
In the words of the development team: we noticed that HttpClient was being used incorrectly by many developers. Net Core
Continuous version rapid upgrade;

Source of problem

For a long time,.net developers have sent HTTP requests by:


using (var httpClient = new HttpClient())
{
 var response = await httpClient.GetAsync(uri);
  
 //do something with response
}

This code theoretically follows the best practice of C#, HttpClient is of type IDisposable, so we use HttpClient by using syntactic sugar. Microsoft’s official documentation also mentions:

As a rule, when you use an IDisposable object, you should declare and instantiate it in a using statement

However, when we tried to run the following test:


public async Task SendRequest() 
{
 Console.WriteLine("Starting reqeust");
 for(int i = 0; i<10; i++)
 {
  using(var client = new HttpClient())
  {
   var result = await client.GetAsync("http://www.baidu.com");
   Console.WriteLine(result.StatusCode);
  }
 }
 Console.WriteLine("Reqeust done");
}

At this time, all ports are listed as follows in terminal:


netstat -ap tcp | grep -i "time_wait"

You will find that 10 ports are opened locally, which means that HttpClient works differently from what we think of as IDisposable. If you use HttpClient as a massive Http request, you will actually create many Http connections, and these resources will not be released immediately. One obvious improvement is to save socket resources by sharing the same HttpClient instance.


private static readonly HttpClient _client = new HttpClient();
public async Task SendRequest2() 
{
 _testOutputHelper.WriteLine("Start request");
 
 for(int i = 0; i<10; i++)
 {
  var result = await _client.GetAsync("http://www.baidu.com");
  Console.WriteLine(result.StatusCode);
 }
 _testOutputHelper.WriteLine("Request done");
}

This solution seems to solve the problem that using the singleton HttpClient does reduce Socket resources, but it raises a new problem: because the Http connection is always connected, it is not applied to the Http connection when the DNS of the request address is updated. This problem is more common in the age of microservices, high availability and Singeton HttpClient doesn’t respect DNS changes.

Eventually, an open source implementation called HttpClientFactory was created to solve this problem once and for all. Microsoft also integrates HttpClientFactory into.net Core.

Create HttpClient in.net Core
1. Add Nuget


Microsoft.Extensions.Http 

2. Register the service in the Dependency Injection container


services.AddHttpClient();

Use IhttpClientFactory with constructor injection


public class BasicUsage
{
 private readonly IHttpClientFactory _clientFactory;

 public BasicUsage(IHttpClientFactory clientFactory)
 {
  _clientFactory = clientFactory;
 }

 public async Task SendRequest()
 {
  var request = new HttpRequestMessage(HttpMethod.Get, 
   "http://www.baidu.com");

  var client = _clientFactory.CreateClient();
  var response = await client.SendAsync(request);
  //do something for response
 }
}

4. Use Named HttpClient

Since we registered a unique HttpClientFactory in the DI container, it means that the HttpClient created through HttpClientFactory may be the same configuration and parameter. If you need different configurations of httpclients, you can register different httpclients by “naming” them.


services.AddHttpClient("baidu", c =>
{
 c.BaseAddress = new Uri("https://www.baidu.com");
 c.DefaultRequestHeaders.Add("Accept", "application/json");
});

Once you have registered an HttpClient named “baidu”, you can create an HttpClient in the following ways:


var client = _clientFactory.CreateClient("baidu");

5. Integrated Polly

Polly is a fault handling library that allows developers to add “retry, fuse, timeout, and so on” policies to Http requests.

Add first Nuget:


Microsoft.Extensions.Http.Polly 

Add strategy:


var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));

services.AddHttpClient("baidu")
 .AddPolicyHandler(request => timeout)
 .AddTransientHttpErrorPolicy(p=>p.RetryAsync(3));

There are also some high-level applications, such as Initiate HTTP requests. In summary, HttpClientFactory provides an efficient and practical way for HttpClient. If you are still in your new HttpClient, please try a new solution.

conclusion

The above is the whole content of this article. I hope the content of this article can be of some reference value to your study or work. If you have any questions, please leave comments and exchange ideas.