preface

About the use of HttpClient, individuals are all come in handy in many scenarios, such as in Winform or back-end services with its call interface to obtain and upload data, micro service using the data sharing between various services, etc., to the moment, seems there is no any problem, but when I saw the official document is introduced using way, Looking back at the code of the previous project, I can only say that I’m glad it didn’t go wrong.

The general meaning of the official document is as follows:

The HttpClient class is simple to use, but in some cases many developers don’t use it correctly; Although this class implements IDisposable, it is not the preferred operation to declare and instantiate it in a using statement because the underlying socket is not immediately released when the HttpClient object is released, which can cause socket exhaustion issues and ultimately SocketException errors. To solve this problem, the recommended approach is to create the HttpClient object as a single object or as a static object

This is a bit disturbing (because some projects use using methods). Although the current concurrency is not enough to cause socketExceptions, it is optimized for a continuous schedule. Let’s explore HttpClient and then IHttpClientFactory.

The body of the

1. HttpClient never seems to work

Here we create a console application for testing (.netcore3.1) with simple code as follows:

Note: the address accessed in the code is a site built on Ali Cloud with Nginx, no load, so it is more intuitive to see the Socket status.

If the Socket is not in use, the Socket is not in use. If the Socket is not in use, the Socket is not in use.

  • Windows test

    Run the following command to view the result:

    netstat -ano | findstr TIME_WAIT
    Copy the code

  • Linux test

    My cloud server was turned on before. The NetCore runtime environment is installed, so create a directory and upload the compiled files to the cloud server via Xftp.

    # note that specify the start here is that the DLL dotnet HttpClientConsoleDemo. DLLCopy the code

    To check the port status, run the following command:

    Netstat ant | grep TIME_WAIT # view port usage, find status to TIME_WAITCopy the code

    In TIME_WAIT state, the system waits for 2MSL (Maximum segment lifetime) before releasing the TCP connection (port). Ensure that ACK resends and discards delayed data. According to the explanation, this approach is reasonable to ensure data transmission, but it does take up resources; Specific knowledge about the network, small partners to look up again.

After Windows and Linux tests, it took about two minutes for the Socket to be released completely. However, if the number of connections per machine is limited in the case of high concurrency, using this way for services and services to exchange data, it will definitely be a problem.

In theory, as long as HttpClient inherits IDisposable interface, the using block can be released after execution. HttpClient does inherit IDisposable. HttpMessageInvoker inherits the IDisposable interface. In this case, the Socket ports allocated by HttpClient are not released in time. To avoid this problem, static variables are recommended.

2. HttpClient is a much better alternative

As per official advice, HttpClient variables are defined as static variables as follows:

By running the program and executing the netstat command, you can see that the resource usage is significantly reduced.

Using static variables seems to solve the problem, but it is not convenient to set different headers for different requests. Other problems I haven’t encountered yet (officials say DNS changes are not supported in this way). Ignore the other questions for now. IHttpClientFactory (IHttpClientFactory, IHttpClientFactory, IHttpClientFactory, IHttpClientFactory, IHttpClientFactory, IHttpClientFactory)

3. IHttpClientFactory works great

IHttpClientFactory is in. NETCore 2.1 provides DefaultHttpClientFactory, which is used to create HttpClient instances for use in applications and automatically maintain the internal HttpMessageHandler pool and its lifecycle. The main functions are as follows:

  • Supports naming and typing configurations to manage configurations in a centralized manner, avoiding conflicts.
  • Flexible configuration of outbound request pipeline, easy to achieve the request life cycle management;
  • Manage the internal HttpMessageHanlder lifecycle to avoid resource usage and DNS refresh
  • Built-in logger

Let’s see how powerful IHttpClientFactory really is;

3.1 Console Demo

Since some services are used internally and are injected in the form of DI, the dependency injection package should be introduced first, as shown in the following figure:

Here’s a quick look at the services you need to sign up for, and we’ll talk about them later:

Run the program, and then check port usage using the following command;

Netstat ano | findstr searches TIME_WAIT # according to the state to find netstat - ano | findstr searches 47.113.204.41 # based on IPCopy the code

Finally did not see very obvious resource occupation, there is a point to force; There are also some more common approaches, demonstrated in the WebApi project (after all, inter-service communication is quite common in microservices).

3.2 WebApi project demo
  • Create an API project and register the related services

  • Add a test controller and test interface

  • Run and see results

    Is it easy to use in API projects, because the project itself has built-in dependency injection related functions, directly registered service can be used; But that doesn’t show how powerful it is, so let’s move on to other extensions.

3.3 Use naming and Type patterns to distinguish HttpClients

The steps are the same as in 3.2, except that they are different when registering the service.

  • Naming pattern

    The code for registering the service is as follows:

    Add a test interface and the result is the same as above, but the HttpClient instance has a different header.

  • Type model

    In fact, the principle of type mode and named mode is the same, but by specifying the type name as the corresponding HttpClient name, reduce the step of defining the name, so it is more convenient, I prefer to use it in this way.

    Start by defining a business processing class, using HttpClient directly, as follows:

    The code for registering a service in Startup is as follows:

    Add a test interface, the result is the same as above, the call process is as follows:

    Enter the TypeHttpClientService corresponding method and see that the header set during registration has taken effect as follows:

    After the command is executed, a result is displayed. Instead of naming HttpClient separately, the type schema uses the class name of the specified type as the default name, which is relatively convenient. The source code for fetching the type name is as follows:

    Small extension: a custom pipe class is not manually registered in Startup.

    AddHttpClient: the Type of the httpclient is registered with the following code:

    In addition to making it easy to distinguish between different HttpClients, if you need to add other common business processes to the request process, you can easily do so by adding custom pipe logic.

3.4 Add custom pipe logic to request pipe
  • Define a pipe class (CustomDelegatingHandler) that inherits DelegatingHandler and override the SendAsync method

  • Register services in Startup and add custom pipes to HttpClient as needed

  • Only HttpClient that uses type mode will be added to the custom pipeline, because it is configured specifically at the time of configuration

    This way is not feeling and. NetCore middleware pipeline is very similar, writing a custom pipeline is similar to writing a middleware (for the principle, subsequent grilled steak source alone), easy do after before the request and response related business process, as the above plus the RequestID, distributed transaction for analysis and micro tracking service invocation chain have very big help, So partners can according to their own needs to encapsulate their own want HttpClient.

4. IHttpClientFactory works perfectly with Polly

As long as the network is involved, it is quite difficult for the service to be 100% stable. For example, problems such as network failure, network fluctuation, remote service failure, and slow response of remote service may affect users’ experience of the system. Don’t panic. Remember Polly libraries designed for failure and resiliency (see Polly- Fault Handling and Resiliency), wouldn’t it be great if HttpClientFactory could work with Polly?

Polly can also be wrapped with HttpClientFactory and Polly, but Microsoft has already built the wheels to work with:

4.1 Importing packages of the corresponding version

The introduction of Microsoft. Extensions. Http. Polly bag, is used here. NetCore3.1, so import the corresponding version as 3.x.

4.2 Specify policies when registering services in Startup

4.3 Adding test interfaces

4.4 Run to see the effect

When the browser accesses the interface, the message will be retried at a specified interval compared to the message printed by the console. The effect is obvious.

The IHttpClientFactory integration with Polly is the same as using Polly alone. For other strategic uses of Polly, see Polly- Good at Troubleshooting and Resiliency.

conclusion

IHttpClientFactory application for the time being, the above mentioned functions are usually used by themselves, other functions can be explored by partners; Later, I will dig the source code with my friends and sort out the process.

If existing projects need to use HttpClient, it is recommended to use the IHttpClientFactory method, which is convenient and flexible, mainly to avoid some problems.

Github address: github.com/zyq025/DotN…

A handsome guy who was made ugly by the program, pay attention to “Code variety circle “, identify attention to learn with me ~~~