.NETCore Remote Call

Reference web address: https://www.cnblogs.com/wudequn/p/13149990.html
HttpClient
HttpClient is a somewhat special object that inherits the IDisposable interface but is shared (or reusable) and thread-safe. From project experience, it is recommended that HttpClient instances be reused throughout the lifecycle of the application rather than instantiated each time an RPC request is made.
    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.

Disadvantages:
1. Because it is a multiplexed HttpClient, some common settings cannot be flexibly adjusted, such as customization of request headers.
2. Because when HttpClient requests each url, the host ip corresponding to that URL is cached, which causes DNS updates to fail (TTL fails)
So is there a way to solve these problems with HttpClient? Until I met the HttpClientFactory, I felt happier writing code instantly. I also lamented that the children's shoes in the new age were so happy that the pits trampled by the older generation could be "perfectly" avoided.
 
Some uses:
        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.

Factory, as its name implies, is the factory of HttpClient. Internal management of HttpClient has been handled for us. We do not need to release objects manually. At the same time, it supports custom request headers, DNS updates, etc.

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.

Recommended Articles

Use:
1. ASP.NET CORE MVC
1. Register httpclient
 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");
                });
            });

  Project Download

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

  Project Download

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

 

  Project Download

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>();
            });
        }

 

 
Classification:   .NET Core

Posted by grigori on Fri, 26 Nov 2021 15:44:04 -0800