HttpClient
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}"); } } } }

Although the project has finished running, the connection still exists, with the status of "time"_ Wait "(keep waiting to see if there are any delayed packets to be transmitted.) , 240 seconds (4 minutes) before the connection is actually closed. For high concurrency scenarios, such as 1000 requests per second, each request uses HttpClient, which will stack 240000 tcp connections in 4 minutes, such a connection explosion will drag down the server. In order to avoid this pit, the usual workaround is to use static HttpClient, but it will bring another unknown problem. When the IP address corresponding to the host name requested by HttpClient changes, HttpClient will be kept in the dark until the application is restarted.
By default, under windows, time_ The wait state will keep the system connected for 240s.
This also leads to a pit I mentioned above: in the case of high concurrency, the connection is too late to release, the socket is exhausted, and a popular error will appear after exhaustion:
Solution reuse HttpClient

Ten become one.
Benefits:
1. As you can see, the original 10 connections have become one. (please don't mind that the target IP of the two examples is different -- SLB (load balancing) results in Baidu IP)
2. In addition, because of the reuse of HttpClient, each RPC request actually saves the time to create a channel, which is also a significant improvement in performance pressure testing. Once, because of this move, the TPS (system throughput) of the web project was increased from 600 to 2000 + in an instant, and the page request time was reduced from 1-3s to 100-300ms, which even made the test team members worship (including the fine tuning of some business codes, of course) , but after knowing the reason, the project performance improvement brought by a small change... Addictive:)
3. As for how to create a static HttpClient for reuse, you can create a "global" static object directly or through various DI frameworks according to the actual project.
1. Because it is a reused HttpClient, some public settings cannot be flexibly adjusted, such as the customization of request headers.
public static async void HttpMul2Async() { //https://docs.microsoft.com/zh-cn/dotnet/api/system.net.http.httprequestmessage?redirectedfrom=MSDN&view=netframework-4.7.2 var request = new HttpRequestMessage(HttpMethod.Get, "www.baidu.com"); //request.RequestUri //request.Headers.Accept; //request.Headers. //HttpRequestHeaders hrh = new HttpRequestHeaders(); //request.Method //StreamContent sc = new StreamContent(); MultipartFormDataContent mfdc = new MultipartFormDataContent(); //mfdc.Add // mfdc.Headers //request.Content = await _client.SendAsync(request); for (int i = 0; i < 10; i++) { var result = await _client.GetAsync("http://www.baidu.com"); Console.WriteLine($"{i}:{result.StatusCode}"); } }
Initial understanding of HttpClientFactory
Introduction:
ASP.NET New features in core 2.1. Installation package Microsoft.Extensions.Http
Httpclientfactory is very efficient and can save system socket to the greatest extent.
According to Microsoft source code analysis, HttpClient inherits from HttpMessageInvoker, which is essentially HttpClientHandler.
HttpClient created by HttpClientFactory, that is, HttpClientHandler. However, these httpclients are put into the "pool". The factory will automatically determine whether to create or reuse each time it creates. (default life cycle is 2min)
If you still can't understand it, you can refer to the relationship between Task and Thread. When you met the problem of HttpClient, you always wondered when Microsoft would officially launch an HttpClient Factory. Although it took so many years until. NET CORE 2.1, it was also exciting.
I ASP.NET CORE MVC
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 = > / / name = client specified here_ 1. It is convenient for us to take this example 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"); }).SetHandlerLifetime(TimeSpan.FromMinutes(5));; services.AddHttpClient(); //other codes services.AddMvc().AddFluentValidation(); } }
2. Use
public class TestController : ControllerBase { private readonly IHttpClientFactory _httpClient; public TestController(IHttpClientFactory httpClient) { _httpClient = httpClient; } public async Task<ActionResult> Test() { var client = _httpClient.CreateClient("client_1"); //Reuse in Startup Defined in client_1 Of httpclient var result = await client.GetStringAsync("/page1.html"); var client2 = _httpClient.CreateClient(); //Create a new one HttpClient var result2 = await client.GetStringAsync("http://www.site.com/XXX.html"); return null; } }
2, Custom request class
1. Define http request 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. Injection
services.AddHttpClient<SampleClient>();
3. Call
public class ValuesController : Controller { private readonly SampleClient _sampleClient;; public ValuesController(SampleClient sampleClient) { _sampleClient = sampleClient; } [HttpGet] public async Task<ActionResult> Get() { string result = await _sampleClient.client.GetStringAsync("/"); return Ok(result); } }
3, Implementation of custom request interface
1. Define request interface, request class
public interface ISampleClient { Task<string> GetData(); } 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<string> GetData() { return await _client.GetStringAsync("/"); } }
When BaseAddress is set, you can write the desired address directly when you request the address.
2. Injection
services.AddHttpClient<ISampleClient, SampleClient>();
3. Call
public class ValuesController : Controller { private readonly ISampleClient _sampleClient;; public ValuesController(ISampleClient sampleClient) { _sampleClient = sampleClient; } [HttpGet] public async Task<ActionResult> Get() { string result = await _sampleClient.GetData(); return Ok(result); } }
HttpClientFactory advanced
Core functions:
• manage the life cycle of the internal HttpMessageHandler (manage socket links), flexibly respond to resource issues and DNS refresh issues
Support named and typed configuration, centralized management of configuration, and avoid conflict
• flexible outbound request pipeline configuration, easy to manage request lifecycle
• built in pipeline outermost and innermost loggers with Information and Trace output
Core objects:
• HttpClient
• HttpMessageHandler
• SocketsHttpHandler
• DelegatingHandler
• IHttpClientFactory
• IHttpClientBuilde
Pipe model:
Similar to the design pattern of middleware. The DelegatingHandler in the middle is middleware processing. The built-in middleware LoggingScopeHttp MesageHandler is located in the outer layer for logging. The innermost LoggingHttp MessageHandler records the inner log. In the middle are middleware that can be customized.
SocketsHttpHandler is where the real request is.
User defined pipeline


public class RequestIdDelegatingHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { //Process request request.Headers.Add("x-guid", Guid.NewGuid().ToString()); var result = await base.SendAsync(request, cancellationToken); //Call internal handler //Process response return result;


public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddControllersAsServices(); services.AddHttpClient(); services.AddScoped<OrderServiceClient>(); services.AddSingleton<RequestIdDelegatingHandler>(); services.AddHttpClient("NamedOrderServiceClient", client => { client.DefaultRequestHeaders.Add("client-name", "namedclient"); client.BaseAddress = new Uri("https://localhost:5003"); }).SetHandlerLifetime(TimeSpan.FromMinutes(20)) .AddHttpMessageHandler(provider => provider.GetService<RequestIdDelegatingHandler>()); services.AddScoped<NamedOrderServiceClient>(); services.AddHttpClient<TypedOrderServiceClient>(client => { client.BaseAddress = new Uri("https://localhost:5003"); }); }
.AddHttpMessageHandler(provider => provider.GetService<RequestIdDelegatingHandler>());
Create HttpClient
1. Factory mode
public class OrderServiceClient { IHttpClientFactory _httpClientFactory; public OrderServiceClient(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<string> Get() { var client = _httpClientFactory.CreateClient(); //use client launch HTTP request return await client.GetStringAsync("https://localhost:5003/OrderService"); } }
_httpClientFactory.CreateClient();
2. Named client mode


public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddControllersAsServices(); services.AddHttpClient(); services.AddScoped<OrderServiceClient>(); services.AddSingleton<RequestIdDelegatingHandler>(); services.AddHttpClient("NamedOrderServiceClient", client => { client.DefaultRequestHeaders.Add("client-name", "namedclient"); client.BaseAddress = new Uri("https://localhost:5003"); }).SetHandlerLifetime(TimeSpan.FromMinutes(20)) .AddHttpMessageHandler(provider => provider.GetService<RequestIdDelegatingHandler>()); services.AddScoped<NamedOrderServiceClient>(); services.AddHttpClient<TypedOrderServiceClient>(client => { client.BaseAddress = new Uri("https://localhost:5003"); }); }
services.AddHttpClient("NamedOrderServiceClient",
The first parameter is the name, and the second is the default configuration.
Where to get httpclient
A kind of httpClientFactory.CreateClient("name");
Advantage: named clients can be configured with different clients for different services. Different clients have different http default configurations. Everyone's socket s are managed separately.
3. Type client mode (Recommended Practice)
In essence, the name is the only difference between the name of the httpclient and the named client. The advantage does not need the string name to get.
Client definition
public class TypedOrderServiceClient { HttpClient _client; public TypedOrderServiceClient(HttpClient client) { _client = client; } public async Task<string> Get() { return await _client.GetStringAsync("/OrderService"); //Use relative path to access } }
Service injection
services.AddHttpClient<TypedOrderServiceClient>(client => { client.BaseAddress = new Uri("https://localhost:5003"); });
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Next, gRPC
Grpc
What is Grpc:
https://baijiahao.baidu.com/s?id=1633335936037018920&wfr=spider&for=pc
Remote call framework
google initiated and open source
Grpc features:
• provide the implementation of almost all mainstream languages and break the language gap
The client can use one language, the server can use one language
• based on HTTP/2, open protocol, widely supported, easy to implement and integrate
• Protocol Buffers serialization is used by default, with much better performance than RESTful Json
Mature tool chain, convenient code generation, out of the box use
It supports two-way flow request and response, and is friendly to batch processing and low latency scenarios
Feel it. The soap experience is coming
Grpc support for. NET
• provide native framework implementation based on HttpClient
• provide native ASP.NET Core integration Library
• provide complete code generation tools
• Visual Studio and Visual StuidoCode provide intelligent prompt of proto file
. NET server reference package
Grpc.AspNetCore
. NET client reference package
• Google.Protobuf
Package of serialization protocol
• Grpc.Net.Client
Client package
• Grpc.Net.ClientFactory
Introducing httpclientfactory
• Grpc.Tools
A package of command line tools is provided to generate our client and server code based on the. proto file
. proto file
• define package and library name
• define service
• define I / O model "message“
This file can generate server code and client code
gRPC exception handling
• use Grpc.Core.RpcException
• use Grpc.Core.Interceptors.Interceptor
gRPC and HTTPS certificate
• use of self-made certificates
• use unencrypted HTTP2
Introduction to proto file
. proto file
syntax = "proto3"; option csharp_namespace = "GrpcServices"; package GrpcServices; service OrderGrpc { rpc CreateOrder(CreateOrderCommand) returns (CreateOrderResult); } message CreateOrderCommand { string buyerId = 1; int32 productId = 2; double unitPrice = 3; double discount = 4; int32 units = 5; } message CreateOrderResult { int32 orderId = 1; }
syntax = "proto3";
Using proto 3 protocol
option csharp_namespace = "GrpcServices";
Indicates that the namespace is GrpcServices
package GrpcServices;
The package defines a scope to prevent naming conflicts between different message types. Just like the name of the line assembly
service OrderGrpc {
rpc CreateOrder(CreateOrderCommand) returns (CreateOrderResult);
}
Define a service name OrderGrpc. The service has a method called CreateOrder
message CreateOrderCommand {
string buyerId = 1;
int32 productId = 2;
double unitPrice = 3;
double discount = 4;
int32 units = 5;
}
message CreateOrderResult {
int32 orderId = 1;
}
The data model is called message. The 1, 2, 3 and 4 in the data model are the field serialization order, which is also used to match the field name.
-------------------------------------------------------------------------------------------------------------------------------------------
When we finish a. proto, it will automatically generate the corresponding code for us. You can see the generated code and the configuration file for comparison.
https://www.jianshu.com/p/da7ed5914088
https://www.cnblogs.com/tohxyblog/p/8974763.html
Server definition


syntax = "proto3"; option csharp_namespace = "GrpcServer"; package orderser; service OrderGrpc { rpc CreateOrder(CreateOrderCommand) returns (CreateOrderResult); } message CreateOrderCommand { string buyerId = 1; int32 productId = 2; double unitPrice = 3; double discount = 4; int32 units = 5; } message CreateOrderResult { int32 orderId = 1; }


public class OrderService : OrderGrpc.OrderGrpcBase { private readonly ILogger<GreeterService> _logger; public OrderService(ILogger<GreeterService> logger) { _logger = logger; } public override Task<CreateOrderResult> CreateOrder(CreateOrderCommand request, ServerCallContext context) { throw new System.Exception("order error"); //Add the internal logic of order creation, input and store the order information in the database return Task.FromResult(new CreateOrderResult { OrderId = 24 }); } }


public void ConfigureServices(IServiceCollection services) { services.AddGrpc( option => { //Production environment shut down option.EnableDetailedErrors = false; //Exception Interceptor option.Interceptors.Add<ExceptionInterceptor>(); } ); }


app.UseEndpoints(endpoints => { //Endpoint map endpoints.MapGrpcService<GreeterService>(); endpoints.MapGrpcService<OrderService>(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); });
Client definition
The client belongs to the caller, and the client belongs to any program. The main purpose is to generate gRPC code and call gRPC just like local method. (the language of client and server can be different)
Console call
Reference package Google.Protobuf Grpc.Net.Client Grpc.Tools
Add the grpc configuration file, which can be copied from the server. Set the application of the project to the file
<ItemGroup> <Protobuf Include="Protos\greet.proto" GrpcServices="Client" /> <Protobuf Include="Protos\order.proto" GrpcServices="Client" /> </ItemGroup>
call
static async Task Main(string[] args) { var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new Greeter.GreeterClient(channel); var reply = await client.SayHelloAsync(new HelloRequest { Name = "Siberian Wolf" }); Console.WriteLine("Greeter Service return data: " + reply.Message); Console.ReadKey(); Console.WriteLine("Hello World!"); }
WEB project call
Introduction package Google.Protobuf Grpc.Net.Client Grpc.Tools Grpc.Net.ClientFactory Grpc.AspNetCore
Add the grpc configuration file, which can be copied from the server. Set the application of the project to the file
<ItemGroup> <Protobuf Include="Protos\greet.proto" GrpcServices="Client" /> <Protobuf Include="Protos\order.proto" GrpcServices="Client" /> </ItemGroup>
Service injection
Like addhttpclient, addgrpcclient
services.AddGrpcClient<OrderGrpc.OrderGrpcClient>(options => { options.Address = new Uri("https://localhost:5001"); });
Grpc uses http2, which is based on https. So how can we change to HTTP? This can use grpc without configuring i certificate.
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); //Allow unencrypted HTTP/2 agreement services.AddGrpcClient<OrderGrpc.OrderGrpcClient>(options => { options.Address = new Uri("http://localhost:5002"); })
There is also the handling of sub signing certificates
Controller call
[Route("api/[controller]")] [ApiController] public class GRPCClientController : ControllerBase { private readonly OrderGrpcClient _orderclient; public GRPCClientController(OrderGrpcClient orderclient) { _orderclient = orderclient; } [HttpGet] public async Task<IActionResult> Get() { var r = _orderclient.CreateOrder(new CreateOrderCommand { BuyerId = "abc" }); return Ok(r.OrderId); } }
Server exception
public void ConfigureServices(IServiceCollection services) { services.AddGrpc(options => { options.EnableDetailedErrors = true; options.Interceptors.Add<ExceptionInterceptor>(); }); }