gRPC 学习笔记
最近因为在开一个新坑,涉及到插件与主程序之间的通信问题。在 Go 官方的 RPC 包和🕊家出的 gRPC 之间摇摆了下后,最终决定选择了 gRPC。但是因为 gRPC 在 2020 年进行了修订,因此网络上的大多数基于旧版本的教程已经用不了了,于是在这里记录一下我的学习过程。
本文主要参照 gRPC 的官方文档和网络资料,所有用到的资料都将在文中标记出处。
gRPC 的安装
安装编译器
↑官方文档是这样说的↑
实际上 emmmm 就用./bin/protoc.exe
就好了。你们如果想用的时候方便一些可以把它加到系统变量里面。
安装 Go protocol buffers 插件
1 | go get google.golang.org/protobuf/cmd/protoc-gen-go |
编译器插件protoc-gen-go
将安装在 $GOBIN
,默认为$GOPATH/bin
。编译器路径必须添加到您的$PATH
中,执行protoc
时才能找到它。[1:1]执行成功似乎不会有什么输出,所以只要顺利结束就好了。
gRPC 入门
走教程
首先,随便写一个hello.proto
,定义一个HelloService
接口[2]
1 | syntax = "proto3"; |
然后编译成 go 的格式
1 | protoc --proto_path=_IMPORT_PATH_ --go_out=_DST_DIR_ --go_opt=paths=source_relative:. |
--proto_path
:_IMPORT_PATH_
表示proto
文件的搜索路径。如果省略,则使用当前目录。还可以通过--proto_path
多次传递选项来指定多个导入目录。将按顺序搜索它们。--proto_path
也可以简写成-I=_IMPORT_PATH_
。[3]--go_out
:指定 go 文件输出的路径_DST_DIR_
。该路径必须已经存在,否则无法正常运行。但该路径下的其他目录如有需要则会被自动创建。[4]--go_opt=paths=source_relative
:让加了option go_package
声明的 proto 文件可以将 go 代码编译到与其同目录,否则会输出到option go_package
中声明的包目录下。[5][6]--go-grpc_opt=paths=source_relative
:同上,不过这个生成的是 grpc 服务文件。--go-grpc_opt=requireUnimplementedServers=false
:禁用 requireUnimplementedServers。默认为启用状态,它的具体用法可以参考这里的内容
比如我这样:
1 | protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_opt=requireUnimplementedServers=false hello.proto |
就会生成hello.pb.go
和hello_grpc.pb.go
文件。
Protocol Buffers 语法学习
Protocol Buffers 目前有 2 和 3 两个版本。因为我主要学习 Protocol Buffers 3,因此下面的内容若无特别注明均为 Protocol Buffers 3 的。
Protocol Buffers 使用 .proto
文件来保存文档。在上面的hello.proto
中已经写出了基本的.proto
文件的结构,下面将详细说明各个部分的写法。
文件的头部
syntax
在.proto
文件的第一个非注释行,用于说明本文件使用的 Protocol Buffers 版本,不填写的话默认为 Protocol Buffers 2。
1 | //使用 Protocol Buffers 3 |
package
下面的一行为这个 proto 文件的包名,在同一文件夹下 proto 文件要声明为同一个package
。**而且同一个包内的不同 proto 文件之间的引用也需要用import
来导入。**不过这个package
仅作用于 proto 文件,和生成的 go 代码无关。(但实际上如果文件没有定义option go_package
的话,编译器会根据package
来猜测要生成的 go 文件的包名)
1 | //声明包名为main |
option go_package
option go_package
用于声明生成的 go 文件所属包的完整包名,例如github.com/wsndshx/MasterService/XXX
啥的,如果是 main 包的话则使用.;main
即可。
1 | option go_package = ".;main"; |
有关于package
和go_package
对生成的 go 文件的影响的详细内容,可以去看看这篇文章:
Message 类型
message
消息对应的是 golang 中的struct
结构体,里面的元素类型除了支持常规的例如int32
string
bool
类型外,还支持repeated
(数组) enum
(枚举) message
(消息) map
(表)。对于 ProtoBuf 支持的类型及其在其他语言中对应的类型列表可以在这里找到:Language Guide | Protocol Buffers | Google Developers
对于一个常规的message
,可以这样去定义
1 | message Latest{ |
写法和定义一个struct
差不多,只不过在每个元素的后面要加上消息编号,一般从1
开始排列。
对于要频繁使用的元素,最好将其消息编号设定在1
~15
之间,因为在这个范围内的元素仅消耗一个字节来编码,而16
到2047
需要消耗两个字节。[7]
repeated
repeated 对于的是各个语言中的数组结构,例如我想通过 gRPC 发送这样的一个数据
1 | { |