Protobuf
用protocolbuffer编译器来编译.proto文件(相当于protobuf语言的源文件)时,编译器将生成所选择语言的代码(作为protobuf语言的编译输出),这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。
基本语法定义
消息message
使用message定义一个消息类型,大部分情况下编译成相应语言后作为一个”类型”。字段
消息中的字段,包括整形(int32、int64、uint32、uint64),浮点(float/double),字符串(string),布尔值(bool)也可以自定义枚举(enum),其它消息类型也可以作为字段。一般编译后为对应语言的字段或属性标识号
每个字段都有唯一的一个标识符(用来索引字段),也就是protobuf包体中是根据标识号作为key来取值的, 标识号和字段名的映射在proto文件编译后由收发两段持有,这样做的好处是可以减少包体大小,提高I/O速度。字段修饰符
只有三种字段修饰符,每个字段必须有字段修饰符。
- required:表示该字段的是必须设置的(该限制体现在:若在对应语言中该字段处于未被赋值/初始化的状态,则会报错)
- optional: 表示该字段的是可选设置的,可通过[default = xxx]指定一个默认值,若没有显示指定默认值并且该字段没有被设置,则会使用该类型的默认值。
- repeated: 表示该字段可以有多个值,一般会被编译为对应语言的集合类或数组。
示例:
message LoginRequest{
required int32 id = 1;
required string password = 2;
}
注释(//)
proto文件可以使用双斜杠(//)的语法格式进行注释。枚举(enum)
使用关键字enum,作用是为字段指定某”预定义值序列”中的一个值,枚举常量必须在32位整形范围内,不推荐用负数,可以在消息内部、消息外部或者其它proto文件中定义枚举,这些枚举可以在任意proto文件中的任何消息定义中复用(前提需要导入定义)扩展(extend)
message User{ required int32 UserId = 1; extensions 5 to 10; } extend User{ required string region = 6; }
包(package)
可以为proto文件添加可选的package声明符,相当于命名空间,用来防止不同消息类型的明明冲突,编译之后会映射到相应语言的命名空间/包上。package module.core; message Content{}
服务(service)
与消息、扩展、枚举都属于一等公民。如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具有一个方法,该方法能够接收 SearchRequest并返回一个SearchResponse,则可进行如下定义:
service SearchService{
rpc Search (SearchRequest) returns (SearchResponse);
}
ProtoBuf编译器会产生一个抽象接口SearchService以及一个相应的存根实现,产生的存根提供了一个类型安全的接口用来完成基于protocolbuffer的RPC调用。所有service类都必须实现对应的Service接口,它提供了一种用来调用具体方法的方式,即在编译期不需要知道方法名及它的输入、输出类型。在服务器端,通过服务注册它可以被用来实现一个RPC Server。
选项(options)
在定义.proto文件时能够标注一系列的options。Options并不改变整个文件声明的含义,但却能够影响特定环境下处理方式。
其它设定说明
导入定义
如果要使用其它proto文件中定义的消息或枚举,则可以通过导入其它proto文件中的定义来使用它们,则需要使用import
关键字添加一个导入声明。类似C/C++中的include 如:import "ecode.proto";
嵌套消息
可以在其它消息类型中定义、使用消息类型,即消息类型的嵌套,这种做法完全符合面向对象的设计思想。更新消息类型
当一个已有的消息格式无法满足新需求,可以在消息中添加额外的字段更新消息类型,并且只要遵循一定的规则,可以不破坏已有代码使旧版本的代码仍然可用。
- 不更改已有字段的标识号
- 添加的字段必须是optional或repeated的(这样不会丢失任何required的元素,保证了良好的兼容性,新代码可以正确与老代码生成的消息交互,老代码只是简单将新字段忽略),逻辑上相当于新的消息类型 “继承” 于老的消息类型。
- 可以移除非required字段,还要保证它们的标识号在新消息类型中不再使用(或重命名要移除的字段,如添加OBSOLETE_前缀),但这个操作需要更改旧代码,但这本身就是必要的,因为要删除的字段说明已经没用了故没有必要在代码中获取。