gRPC-微服務間通訊實踐

微服務間通訊常見的兩種方式

因爲微服務架構慢慢被更多人使用後,迎面而來的問題是如何作好微服務間通訊的方案。咱們先分析下目前最經常使用的兩種服務間通訊方案。git

gRPC(rpc遠程調用)

場景:A服務主動發起請求到B服務,同步方式
範圍:只在微服務間通訊應用github

EventBus(基於消息隊列的集成事件)

技術:NotNetCore.Cap + Rabbitmq + Database
場景:A服務要在B服務作某件事情後響應,異步方式
實現:B服務在完成某件事情後發佈消息,A服務訂閱此消息
範圍:只在微服務間通訊應用web

經過對比,兩種方式徹底不同。rpc是相似於http請求的及時響應機制,可是比http更輕量、快捷,它更像之前的微軟的WCF,能夠自動生成客戶端代碼,充分體現了面向實體對象的遠程調用的思想;Eventbus是異步的消息機制,基於cap的思想,不關心下游訂閱方服務是否消費成功,保障了主服務業務的流暢性,同時也是一款分佈式事務的實現方案,能夠保障分佈式架構中的數據的最終一致性。docker

咱們今天主要介紹gRPC在微服務中的實踐案例。api

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兩個端口。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 的攔截器來統一處理一些事情。

定義協議緩衝區(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.四、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客戶端,而後就可使用裏面的同步或者異步方法啦!

相關文章
相關標籤/搜索