1. gRPC
gRPC是谷歌推出的语言无关、平台无关的高性能RPC库,现已成为微服务集成的主要通讯手段,微软从.net core3.0开始将gRPC作为首选,系统学习可以看微软的文档。关于proto3的语法,这篇写的不错。
2. 编辑proto文件
注意:proto文件应保存到解决方案中(例如Protos文件夹),而不是某项目下面,否则编译时生成的cs文件位置不对。
根据proto3的语法,把原来的业务实体定义为message,远程接口定义为 service。
用 csharp_namespace 指定proto编译得到的类的命名空间。
可以通过 import “***.proto” 的方式引用其他message类型。
proto编译为c#时,属性名自动转为Camol大小写,像u_int32就会编译为UInt32,而UInt32编译为Uint32,所以中间需要大写的记得加_。
例子一,定义 common.proto,存放常用的Empty, RequestId 等。
syntax = "proto3";
option csharp_namespace = "Common";
message Empty{}
message BoolResponse{
bool response = 1;
}
message IntResponse{
int32 response = 1;
}
message StringResponse{
string response = 1;
}
message RequestId{
int32 id = 1;
}
enum ReadWrite
{
None = 0;
Read = 1;
Write = 2;
Both = 3;
}
例子二,业务对象定义tagmessage.proto,引用了common.proto里面的ReadWrite枚举,以及List,Dictionary。
syntax = "proto3";
import "common.proto";
option csharp_namespace = "Models";
message Tag {
uint64 tagid = 1;
string name = 2;
string unit = 3;
string source = 4;
string descr = 5;
ReadWrite rw=6;
double range_low=7;
double range_up=8;
bool compress=9;
string default_value=10;
}
message TagList {
repeated Tag items = 1;
}
message TagMap{
map<uint64,Tag> items = 1;
}
例子三,CRUD 服务接口定义tagrpc.proto。
syntax = "proto3";
import "common.proto";
import "tagmessage.proto";
option csharp_namespace = "Services";
service TagRPC {
rpc GetAll (Empty) returns (TagList);
rpc Get (RequestId) returns (Tag);
rpc GetByName (RequestName) returns (Tag);
rpc Insert (Tag) returns (Tag);
rpc Update (Tag) returns (Tag);
rpc Remove (RequestId) returns (BoolResponse);
}
例子四,使用Nullable类型
import "google/protobuf/wrappers.proto";
...
message TestMessage {
...
google.protobuf.Int32Value nullableInt = 5; //DoubleValue for double?
}
3. 将proto文件添加到.net standara2.1类库
创建 .net standard2.1 类库,用nuget添加Google.Protobuf(3.13.0),Grpc.Net.ClientFactory(2.32.0), Grpc.Tools(2.32.0);
双击工程,在.csproj文件中加入(.net standard2.1项目右键>添加>服务引用 里面没找到 gRPC 选项):
<ItemGroup>
<Protobuf Include="..\Protos\common.proto" GrpcServices="Both">
<Link>Protos\common.proto</Link>
</Protobuf>
<Protobuf Include="..\Protos\tagmessage.proto" GrpcServices="Both">
<Link>Protos\tagmessage.proto</Link>
</Protobuf>
</ItemGroup>
编译工程,如果proto文件语法有错误,根据提示修改。
如果编译通过,那么你就有了一个实体定义的类库,可以被别的 .net 项目引用了。注意,此步骤不是必须的,官方Greeter例子就只有client、server两个项目。
如果需要,可以扩展message生成的类,因为编译出来的是 partial class.
public sealed partial class Tag
{
//扩展
}
4. 将proto文件添加到.net core 服务
新建 Asp.net core gRPC项目,会自动添加Google.Protobuf(3.13.0),Grpc.Net.ClientFactory(2.32.0), Grpc.Tools(2.32.0) 这几个库。
在项目上点右键>添加>服务引用,(批量)选择proto文件,生成类型:服务器。
后面就是 继承 Services.TagRPC.TagRPCBase,override CRUD方法做具体实现了。
5. gRPC不支持null
proto3协议里面不支持null,因为不是所有编程语言都有null,protobuf也不知道怎么对0字节的null进行编码。在proto3文件中可以用Empty代替null,在服务实现时要小心:返回值不能是null,虽然C#允许 await Task.FromResult(null),但经过gRPC时会抛出 RPCException ,状态 Cancelled,所以服务返回值如果是引用类型要返回一个非null实例。
总结
gRPC在微软与谷歌的合作下为.net 开发人员做了很好的整合,虽然先编辑proto再写代码这种方式还需要习惯,但学习成本不高,proto一旦确定那么客户端随便用什么语言都可以写,想想还是很牛叉的。