使用.NET Core创建一个gRPC服务和客户端

2019/10/10

29

.NET Conf 2019 大会正式推出 .NET Core 3.0、C# 8等,随着 .NET Core 3.0 的正式推出,使用 .NET Core 技术创建 gRPC 服务也是 .NET 开发者必会的一项技能,这篇文章会简单的使用一下这项技术,来体验下船新版本。

遗憾的是 Blazor WebAssembly 仍然处于预览阶段,此博客后台前端技术被迫采用 Angular,但说实话,我更想使用 Blazor 技术实现。

关于 gRPC 的简介可以点击这里。废话不多说,开始 Hello World。

Hello World

服务端

首先,我们需要创建一个 gRPC 服务,无脑下一步,VS 会根据 gRPC 模板自动生成一些示例代码。项目创建完毕后,应该如图所示。

项目结构

Protos/*.proto(Protocol Buffers) 是一种灵活、高效、自动序列化结构数据的协议,当前有两个版本,分别是 proto2 和 proto3,两个版本的协议不能完全兼容。proto3 简化了协议使用,生成的协议代码支持更多的编程语言,如 Java、Python、Ruby、Javascript、Object-C、C#等。

Services/*Service/cs 是 C# 对应 proto 的实现,在这些Service文件中编写具体业务代码。

接下来,可以直接按组合键 Ctrl + F5 运行 gRPC 服务。.NET Core 会启动一个控制台,展示信息如下:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Users\HeroWong\source\repos\GrpcService1\GrpcService1

客户端

新建 .NET Core 控制台应用,无脑下一步。

安装 NuGet 包:

新建 Protos 文件夹,拷贝服务端的 greet.protoProtos 文件夹中。浏览复制后的文件,找到以下代码:

option csharp_namespace = "GrpcService1";

注意这里的命名空间,你可以修改它,以确保跟客户端命名空间一致。如果不修改,则需要使用 using 导入它。示例中,我已经将命名空间改成了客户端默认命名空间 ConsoleApp1

在 Programe.cs 中编写以下代码,注意,由于我修改了 proto 文件的命名空间,保持了与客户端默认的一致,所以以下代码并不需要再次 using 导入:

using Grpc.Net.Client;
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client = new Greeter.GreeterClient(channel);
            var reply = await client.SayHelloAsync(new HelloRequest { Name = "HeroWong" });
            Console.WriteLine("Greeting: " + reply.Message);
            Console.ReadKey();
        }
    }
}

接下来,启动客户端程序,得到如下输出:

Greeting: Hello HeroWong

到此,Hello World 结束了。最后再通过现象看本质,如何实现?

实现分析

查看服务端的 Service/GreeterService.cs 代码

public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

GreeterService 从 Greeter.GreeterBase 派生,GreeterBase 是 Greeter 的内部类,二者的类签名如下:

public static partial class Greeter
{
    public abstract partial class GreeterBase
    {
    }
}

可以推测出,Greeter 类的实现是通过 Proto 文件生成的,Proto 文件像是一个“规范的接口”,通过它,可以得到对应的 C# 抽象类,而 Service/GreeterService.cs 继承了这个抽象类,所以必须实现抽象方法,这一趟流程就通了。

评论