最近在学习.net core的微服务体系架构。微服务之间的通信常常通过gRPC进行同步通信,但是需要注意的是,大多数微服务之间的通信是通过事件总线进行异步通信。在微软介绍.net微服务体系架构的项目eShop中,微服务之间进行同步通信的场景很多,大多数都是HTTP/REST,目前只有自定义聚合器与微服务之间通信是使用的gRPC。

整套微服务架构体系,其实除了客户端与网关(BFF)之间,使用HTTP/REST,均可使用gRPC(只要网关支持HTTP/REST与gRPC的转换)

  • BFF转发外部请求
  • 微服务之间
  • 自定义聚合器与微服务

1.gRPC是什么?

A high-performance, open-source universal RPC framework

gRPC是一个高性能的通信协议,它基于HTTP/2protocol buffers。它是微服务之间进行同步通信的主要选择。与之相对的,就是其他协议,如AMQP的异步通信队列或者发布/订阅模式。

RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样.

与HTTP/JSON相比,gRPC的优势:

  • 高性能:协议缓冲区是一种二进制的高性能序列化机制。根据语言的不同,实现协议缓冲区的速度比JSON序列化快8倍,而消息的大小可能比JSON序列化小60%-80%。
  • 支持数据流,说白了,还是快
  • 约定显示,与语言无关:使用proto文件定义服务端与客户端之间的约定

2.在.net core中使用gRPC

在.NET Core 3.0众多更新中,其中有一个重要的更新就是对gRPC的原生支持。从.NET Core3.0开始,无论是开发工具还是框架中,都与gRPC进行了深度的集成,这让使用gRPC的体验如丝般顺滑。

真的有这么丝滑吗?

2.1 工具

工具集成-丝滑享受,主要体现在msbuild,开发者可以直接使用宇宙第一IDE:Vistual Studio或者.NET Core SDK命令dotnet build,通过.proto文件去生成需要的gRPC服务端和客户端代码。这里有两个必要条件:

安装工具包

Google.Protobuf,Grpc.Tools

Install-Package Google.Protobuf -Version 3.12.2
Install-Package Grpc.Tools -Version 2.29.0

.proto的引用

proto文件必须在.csproj中引用。在中使用标签。

<ItemGroup>
   <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
   <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

其中GrpcServices属性:指明生成的代码是客户端还是服务端,或者Both(这是默认值)。当编译代码时(无论是通过运行Visual StudioBuild还是dotnet build),所有代码都将生成并放在obj文件夹中。这是微软故意这样做的,因为这些代码不应该出现在源代码控制存储库中,他们都是生成的,只要.proto文件在,他们都能随时生成。

2.2 实际使用的技巧-亲测有用

参考晓晨博客

主要目标

  • 让客户端和服务端共用一个Protos文件夹,避免重复
  • 使用MSBuild变量在csproj中添加Protobuf标签,避免繁琐修改csproj

服务端

 <ItemGroup>
    <Protobuf Include="..\Protos\*.proto" GrpcServices="Server" Link="Protos\%(RecursiveDir)%(Filename)%(Extension)" />
  </ItemGroup>

客户端

  <ItemGroup>
    <Protobuf Include="..\Protos\*.proto" GrpcServices="Client" Link="Protos\%(RecursiveDir)%(Filename)%(Extension)" />
  </ItemGroup>
  • Include:加通配符的路径,去指定proto文件的路径

image-20200531175946010

3.创建服务端

3.1 安装包

创建gRPC服务端,需要包Grpc.AspNetCore

Install-Package Grpc.AspNetCore -Version 2.29.0

很幸运,VS2019已经为我们准备好服务端的模板。我们可以创建一个gRPC服务端项目。这个模板已经引入了Grpc.AspNetCore包。你可以在模板中搜到的。

image-20200531174439072

3.2 引入proto文件

<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

当然这,模板已经为我们生成了。并且生成了GreeterService.cs

3.3 加入gRPC管道

  • gRPC添加到终结点路由中
app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<GreeterService>();

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

ASP.NET Core 中间件和功能共享路由管道,因此可以将应用配置为提供其他请求处理程序。 其他请求处理程序与已配置的 gRPC 服务并行工作。

  • 注册服务
 public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc();
    }

当然这,模板已经为我们生成了,不要模板自己刀耕火种也是可以的。

4.创建客户端

4.1 安装包

创建gRPC客户端,需要包Google.Protobuf,Grpc.ToolsGrpc.Net.Client

Install-Package Google.Protobuf -Version 3.12.2
Install-Package Grpc.Tools -Version 2.29.0
Install-Package Grpc.Net.Client -Version 2.29.0

4.2 引入与服务端相同的proto文件

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

4.3 编码

static async Task Main(string[] args)
{
    Console.WriteLine("Hello gRPC!");
    AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
            
	var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var greeterClient = new Greeter.GreeterClient(channel);
    
    //https://docs.microsoft.com/zh-cn/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0&WT.mc_id=DT-MVP-5003133
    var reply = await greeterClient.SayHelloAsync(new HelloRequest
    {
        Name = "Garfield"
    });
    Console.WriteLine("Greeter 服务返回数据: " + reply.Message);
    Console.ReadKey();

            
    //var counterClient = new Count.CounterClient(channel);
    //// This switch must be set before creating the GrpcChannel/HttpClient.
    //AppContext.SetSwitch(
    //    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

    //// The port number(5000) must match the port of the gRPC server.
    //var channel = GrpcChannel.ForAddress("http://localhost:5000");
    //var client = new Greet.GreeterClient(channel);
}

5.微服务中使用

如引言所述,gRPC主要用于微服务之间同步通信。

主要运用场景,已经根据业务划分的多个微服务无法满足实际出现的业务场景,需要联合多个微服务进行业务处理。没错,这就是微服务架构体系中的自定义聚合器,实际上聚合器也是一种微服务,负责聚合多个微服务提供较粒度更小的微服务更为强大的自定义微服务。至于使用方式,依然是引入Grpc.Net.Client包,此包本来就是HttpClient基础上实现的,大可以把此当作一个gPRC的HttpClient使用。

6.配置无TLS的gRPC

6.1 服务端配置

gRPC只支持HTTP/2。通常,当客户端连接到服务端时,连接使用HTTP1.1完成,只有当服务器和客户端都支持HTTP/2时才提升为HTTP/2,这就是协议提升,实际上,同类似的, Websocket就是这样通过http操作,走协议提升。这个协议提升使用协议协商执行,通常需要使用ALPN协议实现,这个协议要求必须TLS。

这意味着,在默认情况下,您需要启用一个TLS端点,以便能够使用gRPC。

但是,这里有一个但是,在内部的微服务中,可能是没有启用TLS的,也没必要,因为大家都是自己人,取消TLS,提高效率。在这种情况下,你有两个选择:

  • 打开一个Kestrel,监听HTTP/2
  • 打开两个Kestrel,一个监听HTTP1.1,另一个监听HTTP/2

如果您的服务器除了支持gRPC客户端还必须支持HTTP1.1客户端,则需要第二个选项。下面的代码就是展示了第二种方法(Program.cs):

WebHost.CreateDefaultBuilder(args)
    .ConfigureKestrel(options =>
    {
        options.Listen(IPAddress.Any, 5000, listenOptions =>
        {
            listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
        });
         //Setup a HTTP/2 endpoint without TLS.
        options.Listen(IPAddress.Any, 5001, listenOptions =>
        {
            listenOptions.Protocols = HttpProtocols.Http2;
        });
    })
  • 5000:提供http web api
  • 5001:提供gRPC

6.2 在.NET Core客户端调用

但是,这还不够,我们需要告诉gRPC客户端,客户端可以直接连接到HTTP/2端点,而不需要TLS,口头或者文档说明。

在默认情况下.NET Core不允许gRPC客户端连接到非TLS(non-TLS)端点-不安全的gRPC的服务,十有八九都会报异常

Unhandled exception. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

那么怎么办,就需要如下代码:

// This switch must be set before creating the GrpcChannel/HttpClient.
AppContext.SetSwitch(
    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

// The port number(5000) must match the port of the gRPC server.
var channel = GrpcChannel.ForAddress("http://localhost:5000");
var client = new Greet.GreeterClient(channel);

上述设置只能在客户端开始时设置一次。

7.配置TLS的gRPC

毫无疑问,微服务场景之外的gRPC服务还是需要TLS的。

7.1 方法一:appsetting.json

{
  "Kestrel": {
    "Endpoints": {
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Protocols": "Http2",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      }
    }
  }
}

7.2 方法二:Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                options.Listen(IPAddress.Any, 5001, listenOptions =>
                {
                    listenOptions.Protocols = HttpProtocols.Http2;
                    listenOptions.UseHttps("<path to .pfx file>", 
                        "<certificate password>");
                });
            });
            webBuilder.UseStartup<Startup>();
        });

8.写在末尾

Azure App Service 和IIS目前都还不支持gRPC. 另外Http.Sys也不支持gRPC所依赖的HTTP响应。详情请看github issues

参考链接

https://grpc.io/

https://www.cnblogs.com/shanyou/p/3452938.html

https://blog.csdn.net/yangguosb/article/details/80592777

https://docs.microsoft.com/zh-cn/aspnet/core/grpc/aspnetcore?view=aspnetcore-3.1&tabs=visual-studio

https://docs.microsoft.com/zh-cn/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.1

https://www.cnblogs.com/stulzq/p/11581967.html