• gRPC-微服务间通信实践


    微服务间通信常见的两种方式

    由于微服务架构慢慢被更多人使用后,迎面而来的问题是如何做好微服务间通信的方案。我们先分析下目前最常用的两种服务间通信方案。

    gRPC(rpc远程调用)

    场景:A服务主动发起请求到B服务,同步方式
    范围:只在微服务间通信应用

    EventBus(基于消息队列的集成事件)

    技术:NotNetCore.Cap + Rabbitmq + Database
    场景:A服务要在B服务做某件事情后响应,异步方式
    实现:B服务在完成某件事情后发布消息,A服务订阅此消息
    范围:只在微服务间通信应用

    通过对比,两种方式完全不一样。rpc是类似于http请求的及时响应机制,但是比http更轻量、快捷,它更像以前的微软的WCF,可以自动生成客户端代码,充分体现了面向实体对象的远程调用的思想;Eventbus是异步的消息机制,基于cap的思想,不关心下游订阅方服务是否消费成功,保障了主服务业务的流畅性,同时也是一款分布式事务的实现方案,可以保障分布式架构中的数据的最终一致性。

    我们今天主要介绍gRPC在微服务中的实践案例。

    gRPC-Server(服务端)

    框架介绍

    • .Net Core sdk 3.1
    • Grpc.AspNetCore 2.30.0
    • Grpc.Core 2.30.0

    搭建步骤

    以.net core webapi 项目为例,详细说明如何集成gRPC。

    创建项目

    创建web api项目,此步骤说明省略

    引入nuget包

    引入gRPC 服务端需要的 nuget包,Grpc.AspNetCore 2.30.0和Grpc.Core 2.30.0

    外部访问

    考虑到项目发布后,有webapi本身的http的接口和gRPC的接口都要给外部访问,就需要暴露http1和http2两个端口。

    方式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 + "
    " + ex.StackTrace));
                }
            }
        }
    

    代码中被继承的 Interceptor 是 Grpc.Core.Interceptors.Interceptor。主要处理的目的是把在gRPC接口中抛出的非 RpcException 的异常,转换为 RpcException。此中间件也是根据具体的业务需求来做的,主要是告诉大家可以重写 Grpc.Core.Interceptors.Interceptor 的拦截器来统一处理一些事情。

    定义协议缓冲区(protocol3)

    新建项搜索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

    手动创建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,有定义好的一套状态码和异常封装。

    修改Startup

    在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服务端搭建完成。

    gRPC-Client(客户端)

    框架介绍

    • .Net Core sdk 3.1
    • Google.Protobuf 3.12.4
    • Grpc.Tools 2.30.0
    • Grpc.Net.ClientFactory 2.30.0

    搭建步骤

    以.net core webapi 项目为例,详细说明如何集成gRPC客户端

    创建项目

    创建web api项目,此步骤说明省略

    引入nuget包

    引入gRPC 客户端需要的 nuget包,Google.Protobuf 3.12.4、Grpc.Tools 2.30.0和Grpc.Net.ClientFactory 2.30.0

    引入proto文件

    将服务端的 order.proto 拷贝到客户端的web api项目中,并在csproj文件中添加ItemGroup节点。GrpcServices="Client"表示当前是客户端。改好后重新生成下项目。

    <ItemGroup>
    	<Protobuf Include="Protos/OutpAggregation.proto" GrpcServices="Client" />
      </ItemGroup>
    

    修改Startup

    在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客户端的注入封装,具体代码实现可以查看以下链接。

    Grpc.Net.ClientFactory

    客户端调用

    以在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客户端,然后就可以使用里面的同步或者异步方法啦!

    ReadMe Card

  • 相关阅读:
    rpm 命令|rpm 安装|rpm 卸载|rpm 使用|rpm 删除
    Linux中如何开启8080端口供外界访问 和开启允许对外访问的端口8000
    git远程提交到github或者gitee
    git搭建私有仓库
    Linux命令行设置环境变量
    【Little_things】控制台五子棋(java)
    【cisco实验】练习 2.3.8: 配置基本交换机管理
    操作系统FCFS,SJF进程调度(C++)
    JavaBean的编译和部署说明
    【Python爬虫】爬取个人博客的图片
  • 原文地址:https://www.cnblogs.com/jiangyihz/p/13754406.html
Copyright © 2020-2023  润新知