gRPC - zilor-net/eShopOnContainers-CN-Wiki GitHub Wiki

.NET Core 3.0 的一大新闻是对gRPC的原生支持。 eShopOnContainers 利用 gRPC 进行微服务到微服务的内部同步通信。注意,在 eShop 中,大多数微服务之间的通信都是通过事件总线(支持 RabbitMQ 或 Azure Service Bus)来实现解耦和异步的。gRPC 是一种基于 HTTP/2 和 protobuf 的高性能通信协议。它应该是服务之间直接同步通信的首选(这与其他协议,如用于队列或发布/订阅等异步通信的 AMQP 相反)。

它比使用 HTTP 和 JSON的主要好处是:

  • protobuf 是一种二进制的、高性能的序列化机制。根据语言实现的不同, protobuf 比 JSON 序列化的效率快了 8 倍,而消息大小也小了 60%-80%。
  • 支持数据流
  • 服务和客户机之间的契约是显式的(通过使用proto文件)

gRPC 在 eShopOnContainers 中的用法

在当前的实现中,gRPC的使用仅限于聚合器和微服务之间的通信。

eShopOnContainers 目前在服务之间有以下同步通信:

  1. 外部客户端(即 Xamarin 应用或浏览器)到 Api 网关(BFFs):使用 HTTP/REST
  2. 从 BFFs 到聚合器:使用 HTTP/REST
    • 这基本上是一个请求转发,根据请求路由,它从 BFF 转发到聚合器。
    • 当单个客户端调用涉及从聚合器协调的各种微服务时,这将用于“逻辑复杂”的请求。
  3. 从 BFFs 到微服务: 使用 HTTP/REST
    • 这基本上是一个请求转发,根据请求路由,它被从 BFF 转发到内部微服务。这是为简单的 CRUD 请求执行的。
  4. 从聚合器到微服务:使用 gRPC

目前还没有从 gRPC 到 HTTP/REST 的代码转换。这将允许从 BFFs 到聚合器和微服务使用 gRPC ,同时保持对客户端的 HTTP/REST 接口。gRPC<->HTTP/REST 的转换可以在 BFF 层完成。

以下微服务公开了 gRPC 端点:

  • Ordering API
  • Catalog API
  • Basket API

以下 BFFs 为 gRPC 客户端:

  • Mobile Shopping
  • Web Shopping

在 eShopOnContainers 中的 gRPC 实现

gRPC 是语言无关的:所有的服务都是使用proto文件定义的(通常使用 .proto 的扩展)。这些文件基于protobuffer 语言,并定义了服务的接口。基于这个proto文件,可以为每种语言生成创建服务端和客户端的代码。

从 .NET Core 3 开始,gRPC 已经深度集成到了工具和框架中,以便尽可能的获得无缝使用 gRPC 的体验。

在 .NET Core 3 中从一个原型文件生成服务端或客户端stubs

该工具集成在“msbuild”中(因此它可以被 Visual Studi o和 dotnet build SDK命令使用),它允许基于一个原型文件生成创建 gRPC 服务端或客户端所需的代码。

proto文件必须在csproj中使用<ProtoBuf>标签(在<ItemGroup>内)引用:

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

GrpcServices属性用于指定生成Server还是Clientstubs。

注意需要的话,你可以包括许多 <ProtoBuf> 标签。

当你编译代码时(从 Visual Studio 运行Builddotnet build),所有的代码都会生成并放在obj文件夹中。

这是有意为之的:此代码永远不应该出现在源代码控制存储库中。

创建 gRPC 服务端

服务端 stub 生成的代码,定义了一个抽象基类和一组抽象方法,你必须实现这些方法。

你需要实现 proto文件中定义的每个 rpc 抽象方法:

service Catalog {
  rpc GetItemById (CatalogItemRequest) returns (CatalogItemResponse) {}
  rpc GetItemsByIds (CatalogItemsRequest) returns (PaginatedItemsResponse) {}

它们会生成一个“CatalogBase”抽象类:

public class CatalogService : CatalogBase
{
    public CatalogService()
    {
    }

    public override async Task<CatalogItemResponse> GetItemById(CatalogItemRequest request, ServerCallContext context)
    {
        // Code
    }

    public override async Task<PaginatedItemsResponse> GetItemsByIds(CatalogItemsRequest request, ServerCallContext context)
    {
        // Code
    }
}

参数和返回值所需的 C# 类型也都会自动生成。

将 gRPC 管道添加到 ASP.NET Core

ASP.NET Core 支持直接集成 gRPC 管道。

You only have to use the method MapGrpcService of the IEndpointRouteBuilder in your Startup class:

你只需要在你的 Startup 类中使用IEndpointRouteBuilder的方法MapGrpcService :

app.UseEndpoints(endpoints =>
{
    endpoints.MapDefaultControllerRoute();
    endpoints.MapControllers();
    endpoints.MapGrpcService<CatalogService>();
});

创建 gRPC 客户端

If you are creating a gRPC client instead of a server, you need to create a GrpChannel and then a gRPC client using this channel:

如果你正在创建一个 gRPC 客户端,而不是服务端,那么你需要创建一个GrpChannel,然后使用这个通道创建一个gRPC 客户端:

var channel = GrpcChannel.ForAddress(UrlOfService);
var client = new Basket.BasketClient(channel);

Basket.BasketClient 类是从 proto 文件生成的 stub,你可以调用BasketClient类的方法。

使用 gRPC 而不使用 TLS

gRPC 只支持 HTTP/2 协议。

Usually when a client connects to a server, the connection is done using HTTP1.1 and promoted to HTTP/2 only if both, server and client, support HTTP/2. This promotion is performed using a protocol negotiation, usually implemented using ALPN protocol which requires TLS.

通常,当客户端连接到服务器时,连接是使用 HTTP 1.1 完成的,只有当服务器和客户端都支持 HTTP/2 时,连接才会被提升到 HTTP/2。此提升使用协议协商来执行,通常使用需要 TLS 的 ALPN 协议来实现。

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

然而,在内部的微服务中,可能没有使用 TLS 的端点(因为这些端点都是内部的)。在这种情况下,你有两个选择:

  • 打开单个 Kestrel 端点,在 HTTP/2 上侦听
  • 打开两个Kestrel端点,一个在 HTTP 1.1 上侦听,另一个在 HTTP/2 上侦听

如果你的服务器必须支持除 gRPC 客户端之外的 HTTP 1.1 客户端,就选择第二个。下面的 C# 代码(在Program.cs 中)展示了第二种方法:

WebHost.CreateDefaultBuilder(args)
    .ConfigureKestrel(options =>
    {
        options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
        {
            listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
        });
        options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
        {
            listenOptions.Protocols = HttpProtocols.Http2;
        });

    })

但是,这还不够。我们需要告诉 gRPC 客户端,它可以直接连接到 HTTP/2 端点,而不需要 TLS。

在默认情况 ASP.NET 不允许 gRPC 客户端连接到非 TLS 端点。

客户端需要如下代码行:

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true);

这些设置只能在客户端开始时设置一次。

更多信息

⚠️ **GitHub.com Fallback** ⚠️