Reference web address: https://www.cnblogs.com/wudequn/p/13149990.html
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 in the state of "TIME_WAIT" (continue waiting to see if any delayed packages will be transferred.) and does not really close the connection until 240 seconds (4 minutes). For high-concurrency scenarios, such as 1,000 requests per second, HttpClient is used for each request, and 240,000 tcp connections accumulate within four minutes, such a connection deck can crash the server. To avoid this pit, the usual workaround is to use a static HttpClient, but it creates another notorious problem that HttpClient will be unaware of when it requests a host name corresponding to an IP address change unless the application is restarted.
By default under windows, TIME_ The WAIT status will keep the system connected for 240s.
This leads to one of the pits I've mentioned above: in high concurrency, when the connection is too late to release, the socket is exhausted, and when it's exhausted, a favorite error occurs:
Solution to reuse HttpClient
Ten become one.
Benefits:
1. You can see that the original 10 connections become one connection. (Don't worry if the target IP of the two samples is different - all caused by SLB (load balancing) are Baidu's ips)
2. In addition, because of the reuse of HttpClient, each RPC request actually saves the time of channel creation, which is also a significant improvement in performance measurement. As a result of this action, the TPS (system throughput) of a web project has been improved from 600 seconds per unit to 2000+, and the page request time has been reduced from 1-3s to 100-300ms, which makes small partners of the test group worship (including fine-tuning of some business code, of course), but once you know the cause, a small change improves the performance of the project. Addictive:
3. As to 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 multiplexed HttpClient, some common settings cannot be flexibly adjusted, such as 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 knowledge of HttpClientFactory
Introduction:
New functionality added in ASP.NET CORE 2.1. Installation package Microsoft.Extensions.Http
HttpClientFacotry is efficient and can save system socket s to the maximum.
From Microsoft Source Analysis, HttpClient inherits from HttpMessageInvoker, which is essentially HttpClientHandler.
The HttpClient created by HttpClientFactory is also known as the HttpClientHandler, but these HttpClients are placed in a "pool" where the factory automatically determines whether they are new or reused each time it creates a new one. (Default life cycle is 2 min)
If you don't understand it yet, you can refer to the relationship between Task and Thread. When you encountered the problem of HttpClient before, you were always wondering when Microsoft would officially launch a Factory for HttpClient. Although it took so many years until.NET CORE 2.1, it was still exciting.
1. 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, which makes it easier 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 client_defined in Startup 1 httpclient 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; } }
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. Calls
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. Custom Request Interface Realization
1. Define the 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("/"); } }
With BaseAddress set, when you request an address, you can write directly to the address.
2. Injection
services.AddHttpClient<ISampleClient, SampleClient>();
3. Calls
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 internal HttpMessageHandler (Manage socket links), and be flexible with resource and DNS refresh issues
* Support naming, typing, centralized configuration management, conflict avoidance
* Flexible outbound request pipeline configuration to easily manage request lifecycle
* Built-in pipeline outermost and innermost loggers with Information and Trace output
Core Objects:
• HttpClient
• HttpMessageHandler
• SocketsHttpHandler
• DelegatingHandler
• IHttpClientFactory
• IHttpClientBuilde
Pipeline model:
Design pattern similar to middleware. Middle DelegatingHandler is the middleware process, with the built-in middleware LoggingScopeHttp MesageHandler Located on the outside for logging purposes. The innermost LoggingHttp MessageHandler records the innermost log. Intermediate is a customizable middleware.
Sockets HttpHandler is where the real request comes from.
User-defined Pipeline
public class RequestIdDelegatingHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { //Processing Request request.Headers.Add("x-guid", Guid.NewGuid().ToString()); var result = await base.SendAsync(request, cancellationToken); //Call internal handler //Processing Responses return result;
.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(); //Initiate HTTP requests using client 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 parameter is the default configuration.
Where to get httpclient s
_ httpClientFactory.CreateClient("Name");
Benefits: Named clients allow you to configure different clients for different services, different clients have different http default configurations, and everyone's socket s are managed separately.
3. Typed client mode (recommended practice) Provide different host s for different clients life cycle Pipelines, etc.)
Essentially, as with named clients, the only difference is that the name is used as the name of the httpclient. Benefits don't require a name This string 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 paths here to access } }
Service Injection
services.AddHttpClient<TypedOrderServiceClient>(client => { client.BaseAddress = new Uri("https://localhost:5003"); });
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Here's gRPC
Grpc
What is Grpc:
https://baijiahao.baidu.com/s?id=1633335936037018920&wfr=spider&for=pc
Remote Call Framework
Started by google and Open Source
Grpc features:
* Break language barriers by providing implementation in almost all mainstream languages
Clients can use one language and servers can use one language
* Based on HTTP/2, open protocol, widely supported, easy to implement and integrate
* Protocol Buffers serialization is used by default and performs much better than RESTful Json
* Mature tool chains, easy code generation, out of the box
* Support bidirectional streaming of requests and responses, and be friendly to batch processing, low latency scenarios
Feel the soap experience is coming
Grpc support for.NET
* Provide native framework implementation based on HttpClient
* Provide native ASP.NET Core integration Libraries
* Provide complete code generation tools
* Visual Studio and Visual StuidoCode provide smart tips for proto files
.NET Service-side Reference Package
Grpc.AspNetCore
.NET Client Reference Package
• Google.Protobuf
Package for serialization protocol
• Grpc.Net.Client
Client Package
• Grpc.Net.ClientFactory
Introducing httpclientfactory
• Grpc.Tools
A package that provides command-line tools to generate our client and service-side code based on.proto files
.proto file
Define package, library name
* Define the service "service"
Define the input-output model "message"
This file generates both server-side and client-side code
gRPC exception handling
* Use Grpc.Core.RpcException
* Using Grpc.Core.Interceptors.Interceptor
gRPC and HTTPS Certificates
* Use a homemade certificate
* 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";
proto3 protocol used
option csharp_namespace = "GrpcServices";
Indicates that the namespace is GrpcServices
package GrpcServices;
A package defines a scope that prevents naming conflicts between different message types. Line assembly names are the same
service OrderGrpc {
rpc CreateOrder(CreateOrderCommand) returns (CreateOrderResult);
}
Define a service name, OrderGrpc, and 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, and the 1 inside 2 3 4 Is the sequence of field serialization, also by matching field names.
-------------------------------------------------------------------------------------------------------------------------------------------
When we finish the next.proto, it will automatically generate the corresponding code for us. You can see that the generated code is compared with this configuration file.
https://www.jianshu.com/p/da7ed5914088
https://www.cnblogs.com/tohxyblog/p/8974763.html
Service-side 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 internal logic to create orders, enter and store order information in database return Task.FromResult(new CreateOrderResult { OrderId = 24 }); } }
public void ConfigureServices(IServiceCollection services) { services.AddGrpc( option => { //Production environment shutdown 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 thing is to generate code for gRPC, calling gRPC just like calling a local method. (client and server languages 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 side. Set the project's application to this 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
Introducing packages Google.Protobuf Grpc.Net.Client Grpc.Tools Grpc.Net.ClientFactory Grpc.AspNetCore
Add the grpc configuration file, which can be copied from the server side. Set the project's application to this file
<ItemGroup> <Protobuf Include="Protos\greet.proto" GrpcServices="Client" /> <Protobuf Include="Protos\order.proto" GrpcServices="Client" /> </ItemGroup>
Service Injection
addgrpcclient like addhttpclient
services.AddGrpcClient<OrderGrpc.OrderGrpcClient>(options => { options.Address = new Uri("https://localhost:5001"); });
Grpc uses http2, which is based on https. Then how do we change to http, which allows us to use grpc without configuring an i certificate?
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); //Allow use of unencrypted HTTP/2 protocol services.AddGrpcClient<OrderGrpc.OrderGrpcClient>(options => { options.Address = new Uri("http://localhost:5002"); })
Processing of sub-signature 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 side exception
public void ConfigureServices(IServiceCollection services) { services.AddGrpc(options => { options.EnableDetailedErrors = true; options.Interceptors.Add<ExceptionInterceptor>(); }); }