因为微服务架构慢慢被更多人使用后,迎面而来的问题是如何作好微服务间通讯的方案。咱们先分析下目前最经常使用的两种服务间通讯方案。git
场景:A服务主动发起请求到B服务,同步方式
范围:只在微服务间通讯应用github
技术:NotNetCore.Cap + Rabbitmq + Database
场景:A服务要在B服务作某件事情后响应,异步方式
实现:B服务在完成某件事情后发布消息,A服务订阅此消息
范围:只在微服务间通讯应用web
经过对比,两种方式彻底不同。rpc是相似于http请求的及时响应机制,可是比http更轻量、快捷,它更像之前的微软的WCF,能够自动生成客户端代码,充分体现了面向实体对象的远程调用的思想;Eventbus是异步的消息机制,基于cap的思想,不关心下游订阅方服务是否消费成功,保障了主服务业务的流畅性,同时也是一款分布式事务的实现方案,能够保障分布式架构中的数据的最终一致性。docker
咱们今天主要介绍gRPC在微服务中的实践案例。api
以.net core webapi 项目为例,详细说明如何集成gRPC。安全
建立web api项目,此步骤说明省略服务器
引入gRPC 服务端须要的 nuget包,Grpc.AspNetCore 2.30.0和Grpc.Core 2.30.0架构
考虑到项目发布后,有webapi自己的http的接口和gRPC的接口都要给外部访问,就须要暴露http1和http2两个端口。app
方式1:本地调试时,能够直接暴露http和https,若是你的服务器支持https,也能够在生产环境使用https来访问gRPC服务。框架
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseNLog() .UseUrls("http://*:5000;https://*:5001");
方式2:若是在容器化部署场景下,通常会在dockerfile中指定ASPNETCORE_PORT环境变量,而后程序监听http1和http2两个端口。
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { var aspnetcorePort = Environment.GetEnvironmentVariable("ASPNETCORE_PORT") ?? 5000; int.TryParse(aspnetcorePort, out int port); webBuilder.ConfigureKestrel(options => { options.ListenAnyIP(port, options => options.Protocols = HttpProtocols.Http1); options.ListenAnyIP(port + 1, options => options.Protocols = HttpProtocols.Http2); }) .UseStartup<Startup>(); webBuilder.UseNLog(); });
因为gRPC服务端只能throw 基于 Grpc.Core.RpcException 的异常类型,因此咱们能够自定义中间件来统一处理下异常
using Grpc.Core; using Grpc.Core.Interceptors; using System; using System.Threading.Tasks; public class ExceptionInterceptor : Interceptor { public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>( TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation ) { try { return await continuation(request, context); } catch (RpcException ex) { throw ex; } catch (Exception ex) { throw new RpcException(new Status(StatusCode.Internal, ex.Message + "\r\n" + ex.StackTrace)); } } }
代码中被继承的 Interceptor 是 Grpc.Core.Interceptors.Interceptor。主要处理的目的是把在gRPC接口中抛出的非 RpcException 的异常,转换为 RpcException。此中间件也是根据具体的业务需求来作的,主要是告诉你们能够重写 Grpc.Core.Interceptors.Interceptor 的拦截器来统一处理一些事情。
新建项搜索rpc能够出现协议缓冲区文件
定义示例接口,建立订单方法,以及建立订单入参和出参。关于proto3协议具体说明,请参考往期文章。
syntax = "proto3"; option csharp_namespace = "GrpcTest.Protos"; service Order { rpc CreateOrder (CreateOrderRequest) returns (CreateOrderReply); } message CreateOrderRequest { string ItemCode = 1; string ItemName = 2; string Spec = 3; double Price = 4; double Quantity = 5; string Unit = 6; double Cost = 7; } message CreateOrderReply { bool success = 1; }
在项目的csproj文件中,须要有proto包含进去,GrpcServices="Server"表示当前是服务端。改好后从新生成下项目。
<ItemGroup> <Protobuf Include="Protos/GrpcTest.Protos" GrpcServices="Server" /> </ItemGroup>
手动建立OrderService,继承自Order.OrderBase(proto自动生成的代码)
public class OrderService : Order.OrderBase { public async override Task<CreateOrderReply> CreateOrder(CreateOrderRequest request, ServerCallContext context) { //todo something //throw RpcException异常 throw new RpcException(new Status(StatusCode.NotFound, "资源不存在")); //返回 return new CreateOrderReply { Success = true }; } }
重写CreateOrder方法,此处就能够写你的实际的业务代码,至关于Controller接口入口。若是业务中须要主动抛出异常,可使用RpcException,有定义好的一套状态码和异常封装。
在ConfigureServices方法中加入AddGrpc,以及上面提到的异常处理中间件,代码以下
services.AddGrpc(option => option.Interceptors.Add<ExceptionInterceptor>());
在Configure方法中将OrderService启用,代码以下
app.UseEndpoints(endpoints => { endpoints.MapGrpcService<OrderService>(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("this is a gRPC server"); }); });
至此 gRPC服务端搭建完成。
以.net core webapi 项目为例,详细说明如何集成gRPC客户端
建立web api项目,此步骤说明省略
引入gRPC 客户端须要的 nuget包,Google.Protobuf 3.12.四、Grpc.Tools 2.30.0和Grpc.Net.ClientFactory 2.30.0
将服务端的 order.proto 拷贝到客户端的web api项目中,并在csproj文件中添加ItemGroup节点。GrpcServices="Client"表示当前是客户端。改好后从新生成下项目。
<ItemGroup> <Protobuf Include="Protos/OutpAggregation.proto" GrpcServices="Client" /> </ItemGroup>
在ConfigureServices方法中加入AddGrpcClient,代码以下
services.AddHttpContextAccessor(); AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var baseUrl = "http://localhost:5001/"; services.AddGrpcClient<Order.OrderClient>( options => { options.Address = new Uri(baseUrl); });
注意:要使用.NET Core客户端调用不安全的gRPC服务,须要进行其余配置。 gRPC客户端必须将System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport开关设置为true,并在服务器地址中使用http。能够在如下连接查看具体说明。
[Troubleshoot gRPC on .NET Core]
另外说明下services.AddGrpcClient方法,来自于nuget包Grpc.Net.ClientFactory 2.30.0,将gRPC客户端的注入封装,具体代码实现能够查看如下连接。
以在Controller中调用为例,示例代码以下
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly Order.OrderClient _orderClient; public WeatherForecastController(Order.OrderClient orderClient) { _orderClient = orderClient; } [HttpGet] public async Task<IEnumerable<WeatherForecast>> Get() { var result = await _orderClient.CreateOrderAsync(new CreateOrderRequest { ItemCode = "123", ItemName = "名称1" }); } }
经过构造函数注入gRPC客户端,而后就可使用里面的同步或者异步方法啦!