Protocol Buffer route guide
文章主要内容为介绍使用golang protocol buffer的安装与使用,参考资料有ProtocolBuffer,GRPC route guide。
安装
可以去protoclbuffer官网直接下载安装,也可以使用homebrew这类工具直接安装。
1 | brew install protoc |
此时安装好的是通用件,之后我们再来安装golang的编译插件protoc-gen-go
1 | brew install protoc-gen-go |
或者是使用golang自带的工具进行安装
1 | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest |
基础使用
基本控制
1 | syntax = "proto3"; // 指定版本协议,此处使用的是3的协议 |
使用语法
1 | message Person { // 定义消息类型,会被特定的语言转化为特定类型,例如cpp是转译成class,golang是struct |
在以上例子中,我们注意到Person类型中包含了枚举类型PhoneType以及PhoneNumber类型,不难发现protoc是支持内置类型的。并且内置类型的每个字段(field)的tag都是唯一的,因为传输过程以字段作为标识而不是名字。
并且为例节省字节数在1-15字段中为常用字段,他们在传输时只需要占用1byte的空间,16及以后的字段会占用2byte以上的空间。因此我们要尽量使用小的字段。
默认值
在integer类型中,字段的默认值为0,string的默认值为空字符串,bool类型的默认值为false。
repeated
如果字段被标记为repeated,那么protocol buffer会将其解析为对应类型的动态数组。
Complied
编译对应语言的桩代码,需要对应的插件(golang插件默认安装在GOBIN文件夹中),例如编译成go语言的代码,会默认生成filename.pb.go的桩文件。
编译
1 | protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto |
例如要编译helloworld文件夹中的hello world.proto,我们可以使用以下命令
1 | protoc --proto_path=helloworld --go_out=helloworld helloworld/helloworld.proto |
其中 –proto_path指定源文件路径,**–go_out为要输出的桩代码类型,如果要输出cpp类型的桩代码则使用–cpp_out**
后面跟的是生产文件的输出路径,最后跟的是要编译的文件。因为要编译的文件可能会有依赖文件,因此最开始得指明源文件路径,用于解决依赖。
由于需要编译的文件可能会有相当多的依赖,因此在编译时会以最后指定的输出文件为有限选择。
字段对照表
Trivial type
1 | optional int32 foo = 1; |
1 | int32 foo = 1; |
Message
1 | message Bar {} |
1 | type Baz struct { |
Repeated
1 | message Baz { |
1 | type Baz struct { |
初始化方式
1 | baz := &Baz{ |
MapFields
1 | message Bar {} |
1 | type Baz struct { |
OneofFields
1 | message Profile { |
1 | type Profile struct { |
Enumerations
1 | enum Corpus { |
1 | enum Foo { // type Foo int32 |
FQA
对于不同种类的消息类型,应该如何使用?
"google.golang.org/protobuf/proto"
消息是由协议缓冲区编译器的当前版本生成的所有消息实现的接口类型。对任意消息(如proto.Marshal或proto.Clone)进行操作的函数接受或返回此类型。
"google.golang.org/protobuf/reflect/protoreflect"
Message是描述消息的反射视图的接口类型。对proto.Message调用ProtoReflect方法以获取Protorefect.Message。
"google.golang.org/protobuf/reflect/protoreflect"
ProtoMessage
是”google.golang.org/protobuf/proto”.Message`.的别名,两种消息类型可以互换。
"github.com/golang/protobuf/proto".
消息是由传统GO协议缓冲区API定义的接口类型。所有生成的消息类型都实现此接口,但该接口不描述这些消息的预期行为。新代码应避免使用此类型。
什么是protocbuffer的命名空间类型冲突?
protocbuffer生成的桩代码都会链接到GO二进制的所有协议缓冲区声明都插入到全局注册表中。每个Protobuf声明(例如枚举、枚举值或消息)都有一个绝对名称,它是包名与.proto源文件中声明的相对名称(例如my.Proto.Package.MyMessage.NestedMessage)的串联。Protobuf语言假定所有声明都是全局唯一的。如果链接到GO二进制文件的两个Protobuf声明具有相同的名称,则会导致名称空间冲突,并且注册表不可能按名称正确解析该声明。根据所使用的GO协议错误的版本,这要么会在初始时失败,或者静默地丢弃冲突,并在以后的运行时可能会导致潜在的错误。
如何修复命名空间冲突?
最好方法是人为的更换类型的命名。当然也可以使用以下方法。
当单个.proto文件生成到两个或多个GO包中并链接到相同的GO二进制文件中时,它会在生成的GO包中的每个Protobuf声明上发生冲突。这通常发生在.proto文件被提供并从中生成GO包,或者生成的GO包本身被提供时。用户应避免提供服务,而应依赖于该.proto文件的集中式GO包。
后续
FAQ写不下了,更多请看原文Protocol Buffers