Contents
- The daily use and pits of HttpClient: < ul>
- The advantages of HttpClientFactory:
- Practical usage 1: Conventional usage
- 1. In Startup. Register in cs
- 2. Use, here directly take the controller as an example, and DI in other places by yourself
- Practical usage 2: Use custom classes to execute HttpClientFactory request
- 1. Customize the HttpClientFactory request class
- 2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
- 3. Call:< /li>
- Practical usage 3: To completely encapsulate HttpClient, you can use the following methods
- 1. Customize the HttpClientFactory request class
- 2. In Startup. Register SampleClient in the ConfigureService method in cs, the code is as follows,
- 3. Call:
HttpClient’s daily use and pits:
In C#, when we usually use HttpClient, we wrap HttpClient in using Internal declaration and initialization, such as:
using(var httpClient = new HttpClient() ){ //other codes}
As for why? It’s nothing more than: this is how it is written in the project code, which is how others use it/the same is done in Microsoft’s official ASP.NET tutorial.
Technical point: When you use an object that inherits the IDisposable interface, it is recommended to declare and initialize in the using code block. When the using code segment is executed, the object will be automatically released without the need Manually perform the display Dispose operation.
But here, the HttpClient object is a bit special. Although it inherits the IDisposable interface, it can be shared (or reused) and is thread-safe. From the perspective of project experience, it is recommended to reuse HttpClient instances throughout the life cycle of the application instead of instantiating one every time an RPC request is made. (When I was optimizing a web project in the company, I also had a bug because of HttpClient. I will briefly describe it later.)
Let’s use a simple example to do the test first, and see why not Each RPC request instantiates an HttpClient:
public class Program {static void Main(string[] args) {HttpAsync(); Console.WriteLine("Hello World!") ; Console.Read();} public static async void HttpAsync() {for (int i = 0; i <10; i++) {using (var client = new HttpClient()) {var result = await client.GetAsync(" http://www.baidu.com"); Console.WriteLine($"{i}:{result.StatusCode}");}}}} }
After running the project, check it out through netstate Under the TCP connection status:
-
Although the project has ended, the connection still exists and the status is "TIME_WAIT" (Continue to wait to see if there are any delayed packets that will be transmitted.).
Under windows by default, the TIME_WAIT state will make the system keep the connection for 240s.
-
Here also leads to the pitfall I mentioned above: In the case of high concurrency, the connection is too late to release, and the socket is exhausted. After the end, there will be an error that you like to hear:
#Use jemter to reproduce the error message: Unable to connect to the remote serverSystem.Net. Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.
In the vernacular: "Various socket problems" will appear. (Children's shoes coded WCF may be more memorable and new. The root cause of the problem is to change the soup without changing the medicine.)
The solutions that can be searched in the bear factory are basically "reducing the timeout period", but Artificially reducing the timeout period will cause various inexplicable errors. And it is impossible to avoid the problem of the server crashing sooner or later.
The default value can be modified through the registry: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay])
So how to deal with this problem? The answer has already been said above, just "reuse HttpClient". Such as:
public class Program {private static readonly HttpClient _client = new HttpClient(); static void Main(string[] args) {HttpAsync(); Console.WriteLine("Hello World !"); Console.Read();} public static async void HttpAsync() {for (int i = 0; i <10; i++) {var result = await _client.GetAsync("http://www.baidu. com"); Console.WriteLine($"{i}:{result.StatusCode}");}} }
-
As you can see, the original 10 connections have become 1 connection. (Please don’t care that the target IP of the two examples is different---SLB is caused by Baidu’s ip)
-
In addition, because HttpClient is reused, every RPC request In fact, it also saves the time to create a channel, and it is also significantly improved during performance stress testing. Because of this move, the TPS of the web project was instantly increased from a single 600 to 2000+, and the page request time was also reduced from 1-3s to 100-300ms, which even made the test team's friends worship (of course, it also includes some business The fine-tuning of the code.), but after knowing the reason, a small change brought about the performance improvement of the project. . . It will be addictive:)
-
As for how to create a static HttpClient for reuse, you can follow the actual project, such as creating a "global" static object directly, or through various It can be created using DI-like framework.
But after adjusting the HttpClient reference in this way, there are still some problems that may affect your project (not yet affecting me: P), such as:
< ul>
So is there a way? Solve these problems of HttpClient? Until I met HttpClientFactory, the happiness of writing code instantly increased, and I also felt that the children's shoes of the new era are really too happy, and the pits that the older generation stepped on can be "perfectly" avoided.
HttpClientFactory advantages:
HttpClientFactory is a new feature added in ASP.NET CORE 2.1.
- "Perfectly" solves these pitfalls that I have encountered over the years, and can focus more on business code.
- HttpClientFacotry is very efficient and can save system sockets to the greatest extent. ("JUST USE IT AND FXXK SHUT UP":P)
-
Factory, as the name suggests, HttpClientFactory is the factory of HttpClient. The internal management of HttpClient has been handled for us, and we don’t need to manually manage objects. Release, at the same time, support custom request headers, support DNS updates, etc.
From Microsoft source code analysis, HttpClient inherits from HttpMessageInvoker, and HttpMessageInvoker is essentially HttpClientHandler.
The HttpClient created by HttpClientFactory is also called HttpClientHandler, but these HttpClients are placed in the "pool", and the factory will automatically determine whether it is new or reused every time it is created. (The default life cycle is 2min)
If you still don’t understand, you can refer to the relationship between Task and Thread. When I encountered the problem of HttpClient before, I kept thinking about when Microsoft will officially release a HttpClient Factory. Although it took so many years until .NET CORE 2.1 was released, I was very excited.
How to use HttpClientFactory:
With ASP.NET CORE MVC, you can Convenient use of HttpClient
Practical usage 1: Conventional usage
Unified declaration and configuration in the release project .
1. Register in Startup.cs
public class Startup {public Startup(IConfiguration configuration) {Configuration = configuration;} public IConfiguration Configuration {get;} // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) {//other codes services.AddHttpClient("client_1",config=> //The name specified here=client_1 can facilitate us to take the instance later{ config.BaseAddress = new Uri("http://client_1.com"); config.DefaultRequestHeaders. Add("header_1","header_1"); }); services.AddHttpClient("client_2",config=> {config.BaseAddress = new Uri("http://client_2.com"); config.DefaultRequestHeaders.Add( "header_2","header_2"); }); services.AddHtt pClient(); //other codes services.AddMvc().AddFluentValidation();} }
2. Use, here Take the controller as an example, and DI in other places by itself
public class TestController: ControllerBase {private readonly IHttpClientFactory _httpClient; public TestController(IHttpClientFactory httpClient) {_httpClient = httpClient;} public async TaskTest() {var client = _httpClient.CreateClient("client_1"); //Reuse the httpclient of client_1 defined in Startup var result = await client.GetStringAsync("/page1.html"); var client2 = _httpClient. CreateClient(); //Create a new HttpClient var result2 = await client.GetStringAsync("http://www.site.com/XXX.html"); return null;} }
Practical usage 2: Use a custom class to execute HttpClientFactory requests
1. Customize HttpClientFactory requests Class
public class SampleClient{ public HttpClient Client {get; private set;} public SampleClient(HttpClient httpClient) {httpClient.BaseAddress = new Uri("https://api.SampleClient. com/"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); Client = httpClient; }}
2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
services.AddHttpClient();
3. Call:
public class ValuesController: Controller{ private readonly SampleClient _sampleClient ;; public ValuesController(SampleClient sampleClient) {_sampleClient = sampleClient;} [HttpGet] public async TaskGet() {string result = await _sampleClient.client.GetStringAsync(" /"); return Ok(result); }}
Practical usage 3: fully encapsulating HttpClient, you can use the following method
1. Custom HttpClientFactory request class
public interface ISampleClient{ TaskGetData();} public class SampleClient: ISampleClient{ private readonly HttpClient _client; public SampleClient(HttpClient httpClient) {httpClient.BaseAddress = new Uri("https://api.SampleClient.com/"); httpClient.DefaultRequestHeaders.Add("Accept", "application/ json"); httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); _client = httpClient;} public async Task GetData() {return await _client.GetStringAsync("/");} }
2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
services.AddHttpClient();
3. Call:
public class ValuesController: Controller{ private readonly ISampleClient _sampleClient;; public ValuesController(ISampleClient sampleClient) {_sampleClient = sampleClient;} [HttpGet] public async TaskGet() {string result = await _sampleClient.GetData(); return Ok(result); }}
directory
p>
- Daily use and pits of HttpClient:
- HttpClientFactory advantages:
- HttpClientFactory Usage:
- Practical usage 1: Conventional usage
- 1. Register in Startup.cs
- 2. Use, take the controller as an example directly, and other places Self-DI
- Practical usage 2: Use custom class to execute HttpClientFactory request
- 1. Customize HttpClientFactory request class
- 2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
- 3. Call:
- Practical usage 3: Fully encapsulate HttpClient, you can use the following Method
- 1. Customize HttpClien tFactory request class
- 2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
- 3. Call:
< /ul>
- Practical usage 1: Conventional usage
- Daily use and pits of HttpClient:
- HttpClientFactory advantages:
ul>
- How to use HttpClientFactory:
- Practical usage 1: General usage
- 1. Register in Startup.cs
- 2. Use , Here directly take the controller as an example, and DI in other places by itself
- Practical usage 2: Use a custom class to execute HttpClientFactory requests
- 1. Customize HttpClientFactory requests Class
- 2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
- 3. Call:
- Practical usage 3: To completely encapsulate HttpClient, you can use the following methods
- 1. Customize the HttpClientFactory request class
- 2. Register SampleClient in the ConfigureService method in Startup.cs, the code is as follows,
- 3. Call:
- Practical usage 1: General usage