这是我参与「第五届青训营 」笔记创作活动的第 7 天
一、本堂课主要内容
- 安装并配置proto环境
- 了解proto语法
- 学会通过protoc生成代码
二、详细知识点介绍
Proto
安装ProtoBuf
1、下载protocol buffers Releases · protocolbuffers/protobuf (github.com)
- Protocol buffers,通常称为 Protobuf,是Google开发的一种协议,可以对结构数据进行序列化和反序列化操作,在网络通信中很有用
- 我们下载对应操作系统的zip文件,解压,并将bin目录配置到环境变量中即可
- 最后在cmd或bash中输入
protoc命令查看是否安装并配置成功
2、安装gRPC核心库
go get google.golang.org/grpc
3、安装protocol编译器。它开源生成各种不同语言的代码。因此,除了这个编译器,我们还需要配合各个语言的代码生成工具。对于Golang来说,这个工具是protoc-gen-go。
这里有个小坑,github.com/golang/protobuf/protoc-gen-go和google.golang.org/protobuf/cmd/protoc-gen-go是不同的!前者是旧版本,后者是Google接管的新版本,他们的API是不同的,用于生成的命令,生成的文件都是不一样的。由于目前的gRPC-go源码中的example是使用后者的生成方式,所以我们也采用google版本。
下面我们通过命令安装两个库(因为这些文件在安装grpc时以及下载了,这里只需要install即可)
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Proto文件编写
下面简单写一个小demo,新建两个文件夹,分别作为客户端和服务端。
proto文件内容如下(可以当作模板记下来)
// 使用proto3语法
syntax = "proto3";
// 生成的go文件处于哪个目录哪个包中
// 这里声称在当前目录,service包中
option go_package = ".;service";
// 我们需要定义一个服务,在服务中需要有一个方法,这个方法可以接收客户端参数,返回服务端响应
// 其实很容易可以看出,我们定义了一个service,称为SayHello,这个服务有一个rpc方法,名为SayHello
// 这个方法会发送一个HelloRequest返回一个HelloResponse
service SayHello {
rpc SayHello(HelloRequest) returns (HelloResponse) {}
}
// message关键字,可以理解为结构体
// 这个比较特别的是变量后的"赋值"(这里并不是赋值,而是定义这个变量在message中的位置)
message HelloRequest {
string requestName = 1;
// int64 age = 2;
}
message HelloResponse {
string responseMsg = 1;
}
接下来可以通过protoc生成对应语言的代码,打开Terminal,进入proto目录,输入一些代码即可
两个命令(当作模板可以记下)
protoc --go_out=. hello.proto
protoc --go-grpc_out=. hello.proto
输入完可以发现在proto目录下生成了两个文件,我们使用时只需要重写或修改其中的我们定义的方法,加上业务逻辑即可
Proto文件介绍
message
message: protobuf 中一个定义消息类型是通过关键字 message 指定的。消息就是需要传输的数据格式的定义
message 关键字类似于C++/Java中的class,C/Go中的struct
在消息中承载的数据分别对应每个字段,其中每个字段都有一个名字和一种类型
一个proto文件可以定义多个消息类型
字段规则
required: 消息体中必填字段。不设置会导致编码异常。在protobuf2中使用,在protobuf3中被删去。optional: 消息体中可选字段。protobuf3中没有了reuired,optional等说明关键字,都默认为optional。repeated: 消息体中可重复字段。重复的值的顺序会被保留,在go中重复的会被定义为切片。
消息号
在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[1, 2^29-1]范围内的一个整数。
形式看上去与"赋值"类似。
嵌套消息
可以在其他消息类型中定义、使用消息类型,在下面的例子中,person消息就定义在PersonInfo消息内
message PersonInfo{
message Person{
string name = 1;
int32 height = 2;
repeated int32 weight = 3;
}
repeated Person info = 1;
}
如果要在父消息外重用这个消息类型,需要使用PersonInfo.Person的形式来使用它,如:
message PersonMessage{
PersonInfo.Person info = 1;
}
服务定义
如果要将消息类型用在RPC系统,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。
service SearchService{
# rpc 服务函数名 (参数) 返回 (返回参数)
rpc Search(SearchRequest) returns (RequestResponse)
}
服务端编写
-
创建gRPC Server对象,你可以理解为Server端的抽象对象
-
将 server (其包含需要被调用的服务端接口) 注册到 gRPC Server 的内部注册中心
这样可以在接受到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理
-
创建 Listen,监听TCP端口
-
gRPC Server 开始 lis.Accept,直到 Stop
下面给出以之前写的Hello服务为例,实现以下服务端的编写
// 实现生成代码中未实现的服务
// hello Server
type server struct {
pb.UnimplementedSayHelloServer
}
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
return &pb.HelloResponse{ResponseMsg: "hello" + req.RequestName}, nil
}
三、实践练习例子
实践的部分代码也已经贴在上面了,以及这里是GitHub
四、课后个人总结
其实我对于gRPC的学习已经基本快速入门了! (笔记只上传一点才不是要水笔记呢!)
这几天没课的时候,将会连载gRPC以及有关网络相关的文章~
除了字节官方推荐的Kitex,gRPC或许能成为你的选择~
五、引用参考
gRPC 官方文档中文版_V1.0 (oschina.net)
GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format