Protocol Buffer 是 Google 搞的 RPC 服务的中间层数据协议。其实 RPC 服务之间可以用各种数据格式,例如 JSON、XML 等。但考虑编解码效率和传输效率的话,Protobuf 性能更好。
此外,Protobuf 还包含了一个编译器,可以把 proto 文件编译成各种语言对应的代码。
git clone https://github.com/protocolbuffers/protobuf.git
Protocol Buffer 是 C++ 编写的,主要是安装 g++ 编译器:
sudo apt-get install autoconf automake libtool curl make g++ unzip
cd protobuf/
./autogen.sh
./configure
make
sudo make install
sudo ldconfig
protoc -h
每种语言在使用 Protobuf 时,通过调用现成的库来实现转换。Go 语言的 API 库安装方式:
go get -v -u github.com/golang/protobuf/proto
Protobuf 官方不支持自动生成 Go 代码,需要安装一个插件。从 github 下载后,编译为可执行文件,放到 /bin 目录下即可:
# 安装
go get -v -u github.com/golang/protobuf/protoc-gen-go
# 编译
cd $GOPATH/src/github/golang/protobuf/protoc-gen-go
go build
# 放到 /bin 下
sudo cp protoc-gen-go /bin/
假设当前目录下的 proto 文件名是 first.proto
,执行下面命令:
protoc --go_out=. first.proto
得到 golang 代码 first.pb.go
。proto 会在这个文件里自动创建 Go 语言的 struct,在项目中引入即可使用。
可以参考: https://colobu.com/2019/10/03/protobuf-ultimate-tutorial-in-go/
Protobuf 的使用流程:
推荐使用 proto3 语法,必须明确声明。如果没有声明,默认使用 proto2 语法。
proto 文件由一个个具体的消息构成,包括请求消息和响应消息。每个消息则包含了各种类型的字段。
消息的每个字段包含前置的类型、字段名、Tag 标签。
每个字段前,可以加 repeated 关键字,表示该字段是数组。
syntax = "proto3";
// hello 请求
message HelloRequest {
string name = 1; // 姓名
int32 age = 2; // 年龄
repeated string job = 3; // 工作
}
message HelloResponse {
int32 no = 1;
}
Tag 标签从 1 开始,定义后不可修改。[1-15]内的标识在编码时占用一个字节,包含标识和字段类型,对于常用字段最好放在前 15 个字段。
[19000-19999]之间的标识符是 proto 预留的,不可使用。
import "other.proto";
项目太大时,难免有命名冲突。可以通过定义 proto 的包名避免 message 类型之间的名字冲突。
package foo.bar;
可以使用 reserved 关键字指定保留字段名和标签。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
除了基本数据类型外,proto 还支持复合数据类型,包括:枚举、map、数组、其它message。
.proto | Go |
---|---|
double | float64 |
float | float32 |
int32 | int32 |
int64 | int64 |
uint32 | uint32 |
uint64 | uint64 |
sint32 | int32 |
sint64 | int64 |
fixed32 | uint32 |
fixed64 | uint64 |
sfixed32 | int32 |
sfixed64 | int64 |
bool | bool |
string | string |
bytes | []byte |
使用 Oneof 定义的一组字段,最多允许这组字段中的一个出现。
syntax = "proto3";
package abc;
message OneofMessage {
oneof test_oneof {
string name = 4;
int64 value = 9;
}
}
oneof 字段不能同时使用repeated。
proto3 的 map 语法为 map
,例如:
map m_name = 1;
枚举类型限定字段的值只能取某个特定的值,比如星期类型只能取周一到周日七个值。
枚举类型的定义采用C++ scoping规则,也就是枚举值是枚举类型的兄弟类型,而不是子类型,所以避免在同一个package定义重名的枚举字段。
可以把 message 类型当做普通字段,嵌套使用。
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}