一. protobuf包的安装
1.下载protoc
编译器
下载链接: protocolbuffers/protobuf
解压后压缩包中有两个文件夹: bin
和include
, 将bin
目录下的protoc.exe
文件拷贝到GOPATH/bin
目录下, 将include
目录下的google
文件夹拷贝到GOPATH/src
目录下(其实可以放在任意位置,因为编译时需要手动指定该文件夹的路径, 下面再说)
2. 安装golang编译插件protoc-gen-go
protoc
程序会调用protoc-gen-go
插件, 将.proto
文件自动生成golang
代码, .proto
文件中的message
会生成golang结构体, 并具有相关的getter,setter等方法, 可以直接在golang代码中引用.
通过go get
命令安装(会同时安装google.golang.org/protobuf
模块):
go get google.golang.org/protobuf/cmd/protoc-gen-go
安装成功后会在GOPATH/bin
下生成protoc-gen-go.exe
可执行文件.
二. 编写proto文件
通过官方Tutorials中的一个列子来说明:
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
option go_package = "./addressbook";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
message AddressBook {
repeated Person people = 1;
}
其中:
-
syntax
设置语法类型,proto2
和proto3
存在语法差异. -
package addressbook
标识包名称, 防止不同项目间命名冲突(暂未发现有什么用). -
import
引用其他proto
文件, 如本例中引用了官方的时间戳类型. -
go_package
设置生成的go文件的包路径(在编译时细说). - 声明
message
, 相当于golang中的struct
, 每个message
可以包含多个字段;message
可以嵌套声明, 如PhoneNumber
, 也可以声明后在其他message
中使用, 如AddressBook
中的Person
. -
message
中的字段声明, 标量字段(如string name = 1
)与golang中的字段类型一一对应(参考链接);
message类型字段(如repeated PhoneNumber phones = 4
);
枚举字段(如enum PhoneType{...}
), 枚举值必须从0开始;
repeated字段对应golang中的slice
(如repeated Person people =1
, 即people []*Person
);
map字段对应golang中的map
类型, 声明格式map
对golang中的map[string]int64
1.关于
package addressbook
这一句, 不少文章都说它设置了生成的go代码的包名, 实际go的包名是由go_package
或者编译命令参数--go_opt=M
影响, 这一句就像上面说的,仅仅是名称标识, 可有可无, 不对结果产生影响.
- 关于字段上的
=1
,= 2
, 官方文档说是每个元素在二进制编码中的标记, 但是并没理解什么意思, 也就照着写了.
三. 编译
先说明版本环境: [email protected], [email protected]
版本差异可能导致编译命令失效或报错, 我就是因为网上查了很多文章都不好使才写了这篇笔记-_-
我是在proto
文件所在目录下执行的命令:
protoc -I=D:\Code\Go\src -I=. --go_out=. addressbook.proto
-
-I
即--proto_path
, 指定要引用的proto
文件路径.
第一个-I=D:\Code\Go\src
就是上面说的protoc
源码中include
目录下google
文件夹放置的位置, 也就是import "google/protobuf/timestamp.proto"
引用官方类型的位置, 如果不加这一句会报这样的错误:
google/protobuf/timestamp.proto: File not found.
addressbook.proto:3:1: Import "google/protobuf/timestamp.proto" was not found or had errors.
addressbook.proto:23:3: "google.protobuf.Timestamp" is not defined.
所以我上面说google
文件夹可以放在任意位置, protoc
编译时似乎并不会识别GOPATH
(或许是我没搞明白).
如果google
文件夹放在你编写的proto
文件的同级目录下, 可以省掉这一个参数.
- 第二个
-I=.
指明了要编译的proto
文件的路径, 这个有点脱裤子放屁的感觉, 因为命令最后一个参数同样是要编译的proto
文件的路径即文件名, 但是不加会报错:
addressbook.proto: File does not reside within any path specified using --proto_path (or -I). You must specify a --proto_path which encompasses this fi
le. Note that the proto_path must be an exact prefix of the .proto file names -- protoc is too dumb to figure out when two paths (e.g. absolute and rel
ative) are equivalent (it's harder than you think).
-
--go_out=.
参数, 其中--go_out
设置protoc
使用golang插件进行编译即protoc_gen_go
, 其他语言也有对应的参数即插件. 而--go_out
参数的值则指定了生成的go文件的路径, 这个就要与proto
文件中的go_package
配合, 例如我的这条命令就会生成./addressbook/addressbook.pb.go
.
可以这样理解,--go_out
指定项目根目录,go_package
指定go的包名和路径, 最后go文件是proto
同名文件加".pb.go"后缀.
如果
proto
文件中不写go_package
, 可以在命令中添加参数--go_opt=M{proto文件路径}={go包路径}
, 例如:--go_opt=M./addressbook.proto=./addressbook
, 与上面命令相同效果.
- 最后就是
proto
文件的路径了.
四. 在go代码中使用protobuf
package main
import (
pb "demos/protobuf_demo/addressbook" // 上面由proto编译生成的go文件
"google.golang.org/protobuf/proto" // 在安装protoc_gen_go时一并安装了该golang包
"os"
)
func main() {
// 初始化AddressBook
p := pb.Person{
Id: 1234,
Name: "Isaac",
Email: "[email protected]",
Phones: []*pb.Person_PhoneNumber{
{Number: "555-4321", Type: pb.Person_HOME},
},
}
addressBook := &pb.AddressBook{
People: []*pb.Person{&p},
}
// 将AddressBook对象序列化
out, err := proto.Marshal(addressBook)
if err != nil {
panic(err)
}
// 将序列化后的数据写入文件
if err = os.WriteFile("address", out, 0644); err != nil {
panic(err)
}
// 从文件中读取数据
in, err := os.ReadFile("address")
if err != nil {
panic(err)
}
// 将数据反序列化为AddressBook对象
address := &pb.AddressBook{}
if err = proto.Unmarshal(in, address); err != nil {
panic(err)
}
}
参考:
https://developers.google.com/protocol-buffers/docs/gotutorial
https://developers.google.com/protocol-buffers/docs/reference/go-generated