文章目录
- 1.HttpClientFactory :管理外向请求的最佳实践
- 1.1 核心能力
- 1.2 核心对象
- 1.3 HttpClient创建模式
- 2.gRPC:内部服务间通讯利器
- 2.1 什么是gRPC
- 2.2 特点gRPC特点
- 2.3.NET生态对gRPC的支持情况
- 2.4 服务端核心包
- 2.5 客户端核心包
- 2.5 .proto文件
- 2.6 gRPC异常处理
- 2.7 gRPC与HTTPS证书
- 2.8 gRPC命令行工具
- 2.8.1 工具核心包
- 2.8.2 核心命令
- 2.8.3 最佳实践
1.HttpClientFactory :管理外向请求的最佳实践
1.1 核心能力
- 管理内部HttpMessageHandler的生命周期,灵活应用资源问题和DNS刷新问题
- 支持命名化、类型化配置,集中管理配置,避免冲突
- 灵活的出站请求管道配置,轻松管理请求声明周期
- 内置管道最外层和最内层日志记录器,有Information和Trace输出
1.2 核心对象
- HttpClient
- HttpMessageHandler
- SocketsHttpHandler
- DelegatingHandler
- IHttpClientFactory
- IHttpClientBuilder
管道模型
请求过程
HttpClient发起请求,然后最外层的日志记录器来记录日志,然后再进到自定义的Handler中处理自定义的逻辑;
然后最内层的SocketsHttpHandler,这是真正去发起远程调用的处理程序,它回向远程站点发起HTTP请求并接受响应,在接收到响应以后,Http最内层的日志记录器会记录响应信息;
随后将响应结果交还给自定义的Handler,在接受响应后处理接受响应的逻辑,处理完成后最外层的日志记录器会输出响应日志,最终HttpClient拿到响应结果,输出给应用程序
1.3 HttpClient创建模式
- 工厂模式
- 命名客户端模式
- 类型化客户端模式
// 工厂模式
public class OrderServiceClient
{
IHttpClientFactory _httpClientFactory;
public OrderServiceClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<string> Get()
{
var client = _httpClientFactory.CreateClient();
return await client.GetStringAsync("这里是远程服务路径地址");// 相对路径访问
}
}
// 命名客户端
public class NamedOrderServiceClient
{
IHttpClientFactory _httpClientFactory;
const string _clientName = "NamedOrderServiceClient";
public NamedOrderServiceClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<string> Get()
{
var client = _httpClientFactory.CreateClient(_clientName);
return await client.GetStringAsync("这里是远程服务路径地址");// 相对路径访问
}
}
// 类型客户端
public class TypeOrderServiceClient
{
HttpClient _client;
public TypeOrderServiceClient(HttpClient client)
{
_client = client;
}
public async Task<string> Get()
{
return await _client.GetStringAsync("这里是远程服务路径地址");// 相对路径访问
}
}
// HttpClientFactory注册,startup中ConfigurationService
public void ConfigureServices(IServiceCollection services)
{
// 工厂模式
services.AddHttpClient();
services.AddScope<OrderServiceClient>();
// 命名客户端模式
services.AddHttpClient("NamedOrderServiceClient",client =>
{
client.DefaultRequestHeaders.Add("client-name","nameclient");
client.BaseAddress = new Uri("远程站点根路径");
})
.AddHttpMessageHandler(provider => provider.GetService<RequestIdDelegatingHandler>());//自定义Handler
services.AddScope<NamedOrderServiceClient>();
//推荐使用
// 类型客户端,
services.AddHttpClient<TypeOrderServiceClient>(client =>
{
client.BaseAddress = new Uri("远程站点根路径");
});
}
// Controller中使用
public class OrderController : ControllerBase
{
OrderServiceClient _orderServiceClient;
public OrderController(OrderServiceClient orderServiceClient)
{
_orderServiceClient = orderServiceClient;
}
[HttpGet("Get")]
public Task<string> Get()
{
return await _orderServiceClient.Get();
}
[HttpGet("NameGet")]
public Task<string> NamedGet([FromServices] NamedOrderServiceClient serviceClient)
{
return await serviceClient.Get();
}
[HttpGet("TypeGet")]
public async Task<string> TypeGet([FromServices] TypeOrderServiceClient client)
{
return await client.Get();
}
}
2.gRPC:内部服务间通讯利器
2.1 什么是gRPC
- 定义
gRPC是一个远程过程调用框架,作用是让我们可以像在调用本地的类一样调用远程的服务。由Google公司发起并开源,g表示Google公司,RPC表示远程调用
2.2 特点gRPC特点
- 提供几乎所有主流语言的实现,打破语言隔阂
- 基于HTTP/2,开放协议,收到广泛的支持,易于实现和集成
- 默认使用ProtocolBuffers序列化,性能相较于RESTfulJson好很多
- 工具链成熟,代码生成便捷,开箱即用
- 支持双向流式的请求和响应,对批量处理、低延时场景友好
2.3.NET生态对gRPC的支持情况
- 提供基于HttpClient的原生框架实现
- 提供原生的ASP.NETCore集成库
- 提供完整的代码生成工具
- Visual Studio和Visual Studio Code提供proto文件的只能提示
2.4 服务端核心包
- Grpc.AspNetCore
2.5 客户端核心包
- Google.Protobuf ⇒ 序列化协议包
- Grpc.Net.Client ⇒ 客户端包
- Grpc.Net.ClientFactory ⇒ 与HttpClientFactory集成的包
- Grpc.Tools ⇒ 提供命令行工具使用的包
2.5 .proto文件
- 定义包、库名
- 定义服务“service”
- 定义输出和输入模型“message”
2.6 gRPC异常处理
- 使用Grpc.Core.RpcException
- 使用Grpc.Core.Interceptors.Interceptor
RpcException支持拦截器,可以通过注入拦截器处理异常
2.7 gRPC与HTTPS证书
- 使用自制证书
- 使用非加密的HTTP2
服务端
// order.proto文件定义 *****核心*****
syntax = "proto3";// 定义proto协议类型为proto3
// 定义命名空间为GrpcServices
option csharp_namespace = "GrpcServices";
package GrpcServices;
// 定义服务OrderGrpc
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;
}
// 定义OrderService
public class OrderService : OrderGrpc.OrderGrpcBase
{
public override Task<CreateOrderResult> CreateOrder(CreateOrderCommand request, ServerCallContext context)
{
throw new System.Exception("order error");
//添加创建订单的内部逻辑,录入将订单信息存储到数据库
return Task.FromResult(new CreateOrderResult { OrderId = 24 });
}
}
// 注册gRPC服务
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.EnableDetailedErrors = true;// 内部错误信息输出设置,生产环境不对外输出
options.Interceptors.Add<ExceptionInterceptor>();// 添加的异常拦截器
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<OrderService>();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
定义好order.proto文件,那么会自动生成服务端代码,生成的代码在项目目录的obj文件夹下的Order.cs和OrderGrpc.cs
客户端
将服务端的order.proto文件引入客户端,可以基于proto文件生成客户端代码
// startup
public void ConfigureServices(IServiceCollection services)
{
//允许使用不加密的HTTP/2协议
//AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
// 注册OrderGrpcClient
// OrderGrpcClient文件是由order.proto文件生成的
services.AddGrpcClient<OrderGrpc.OrderGrpcClient>(options =>
{
options.Address = new Uri("https://localhost:5001");
})
.ConfigurePrimaryHttpMessageHandler(provider =>
{
var handler = new SocketsHttpHandler();
handler.SslOptions.RemoteCertificateValidationCallback = (a, b, c, d) => true; //允许无效、或自签名证书
return handler;
}).AddTransientHttpErrorPolicy(p => p.WaitAndRetryForeverAsync(i => TimeSpan.FromSeconds(i * 3)));
}
2.8 gRPC命令行工具
2.8.1 工具核心包
- Grpc.Tools ⇒ 工程需要引用的工具
- dotnet-grpc ⇒ 命令行工具,是.net命令行的工具插件
2.8.2 核心命令
- dotnet grpc add-file ⇒ 将指定目录下的proto文件添加到工程中
- dotnet grpc add-url ⇒ 将一个HTTP的URL地址指定的proto文件添加到我们的工程中
- dotnet grpc remove ⇒ 将添加的proto文件的引用移除,文件不会移除
- dotnet grpc refresh ⇒ 更新proto文件
2.8.3 最佳实践
- 使用单独的Git仓库管理proto文件
- 使用submodule将proto文件集成到工程目录中
- 使用dotnet-grpc命令行添加proto文件及相关依赖包引用
备注
由proto生成的代码文件会存放在obj目录中,不会被签入到Git仓库