初识gRPC

gRPC和RESTful API是两种流行的服务通信方案,相比RESTful API,gRPC性能更强、开发效率更高、跨语言支持更完善,在许多场景下得到应用。

本文以一个实例作为切入点,对gRPC的整体情况进行一个简要介绍。

项目搭建

服务端:

  • 创建ASP.NET Core gRPC服务项目
  • 创建proto文件,自动生成后台代码
  • 创建服务(依赖自动生成的后台代码)
  • 在主函数中对服务进行发布

客户端:

  • 创建控制台程序
  • 安装NuGet包

  • 拷贝服务端的proto文件,自动生成客户端代码
  • 在主函数中直接调用远程服务

小实例

proto文件

以下proto文件创建了两个远程调用函数,并且定义了四个消息用于传参和返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
syntax = "proto3";

option csharp_namespace = "GrpcService1";

package greet;

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc Compute (ComputeData) returns (ComputeResult);
}

message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;
}

message ComputeData{
string name = 1;
int32 age = 2;
repeated string hobbies = 3;
map<string, int32> scores = 4;
Address address = 5;
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
Gender gender = 7;
}

message ComputeResult{
string res=1;
}

message Address {
string city = 1;
string street = 2;
}
  • proto中的message类似C#中的class,可以包含普通数据类型、数组、字典、嵌套message和枚举
  • message的定义是使用gRPC的核心之一

服务端代码

服务实现

定义服务类,继承自动生成的类,重载远程调用函数即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}

public override Task<ComputeResult> Compute(ComputeData request, ServerCallContext context)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"姓名:{request.Name}");
sb.AppendLine($"年龄:{request.Age}");
sb.Append($"爱好:");
foreach (var hobby in request.Hobbies)
sb.Append($"{hobby} ");
sb.AppendLine();
sb.Append($"成绩:");
foreach (var (key,value) in request.Scores)
sb.Append($"{key}-{value} ");
sb.AppendLine();
sb.AppendLine($"地址:{request.Address.City},{request.Address.Street}");
sb.Append($"性别:{request.Gender.ToString()}");
return Task.FromResult(new ComputeResult { Res = sb.ToString() });
}

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

主函数

在主函数中对上述服务类进行注册:

1
2
3
4
5
6
7
8
9
10
11
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();

app.MapGrpcService<GreeterService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client.");

app.Run();
}

客户端代码

在客户端拷贝相同的proto文件,并自动生成客户端后台代码,直接创建客户端调用远程函数即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using Grpc.Net.Client;
using GrpcGreeterClient;

var channel = GrpcChannel.ForAddress("https://localhost:7109");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "李浩" });

var data = new ComputeData()
{
Name = "李浩",
Age = 31,
Hobbies = { "跑步", "看书", "编程" },
Scores = { { "数学", 140 }, { "语文", 130 } },
Address = new Address
{
Street = "和平大道",
City = "武汉市",
},
Gender = ComputeData.Types.Gender.Male,
};

var computeReply = await client.ComputeAsync(data);

Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Compute Result:" + computeReply.Res);
Console.ReadKey();

proto生成代码探究

对于以下简单的proto文件:

1
2
3
4
5
6
7
8
9
10
11
12
syntax = "proto3";
option csharp_namespace = "GrpcService1";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}

在服务端会生成如下图所示的代码元素:

在客户端会生成如下图所示的代码元素:

使用proto文件的心智简化:

  • 在服务端会自动生成一个抽象类(是嵌套类),其中定义了远程调用函数,用户需要定义新的服务类并具体实现该远程调用函数
  • 在客户端会自动生成一个类,其中会包含远程调用函数的多版本实现,用户直接调用这些函数就可以与服务端通信
  • 在proto文件中定义的message会转换成C#类,字段也会相应映射
  • 我们在使用gRPC时,只需要牢记以上三点并熟练掌握proto编写语法即可

(转载本站文章请注明作者和出处lihaohello.top,请勿用于任何商业用途)

评论