【详细解析版】Protobuf3使⽤⼿册
Protobuf使⽤⼿册--中⽂版,好不容易到了,分享⼀下给⼤家。
⽬录
第1章定义.proto ⽂件
⾸先我们需要编写⼀个 proto ⽂件,定义我们程序中需要处理的结构化数据,在 protobuf 的术语中,结构化数据被称为 Message。proto ⽂件⾮常类似 java 或者 C 语⾔的数据定义,可以使⽤C或C++风格的注释。下⾯是⼀个proto⽂件的例⼦。
syntax = "proto3";
package tutorial;
option java_package = "ample.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2; // Unique ID number for this person.
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person person = 1;
}
⼀个proto⽂件主要包含package定义、message定义和属性定义三个部分,还有⼀些可选项。
⽂件的第⼀⾏指定了你正在使⽤proto3语法:如果你没有指定这个,编译器会使⽤proto2。这个指定语法⾏必须是⽂件的⾮空⾮注释的第⼀个⾏。
1.1 定义package
Package在c++中对应namespace。
对于Java,包声明符会变为java的⼀个包,除⾮在.proto⽂件中提供了⼀个明确有java_package。
1.2 定义message
Message在C++中对应class。Message中定义的全部属性在class中全部为private的。
Message的嵌套使⽤可以嵌套定义,也可以采⽤先定义再使⽤的⽅式。
Message的定义末尾可以采⽤java⽅式在不加“;”,也可以采⽤C++定义⽅式在末尾加上“;”,这两种⽅式都兼容,建议采⽤java定义⽅式。
向.proto⽂件添加注释,可以使⽤C/C++/java风格的双斜杠(//) 语法格式。
1.3 定义属性
属性定义分为四部分:标注+类型+属性名+属性顺序号+[默认值],其⽰意如下所⽰。
标注类型属性名属性顺序号[默认值]
required string name= 1[default=””];
其中属性名与C++和java语⾔类似,不再解释;下⾯分别对标注、类型和属性顺序号加以详细介绍。
其中包名和消息名以及其中变量名均采⽤java的命名规则——驼峰式命名法,驼峰式命名法规则见附件1。
1.3.1 标注
标注包括“required”、“optional”、“repeated”三种,其中
required表⽰该属性为必选属性,否则对应的message“未初始化”,debug模式下导致断⾔,release模式下解析失败;
optional表⽰该属性为可选属性,不指定,使⽤默认值(int或者char数据类型默认为0,string默认为空,bool默认为false,嵌套message默认为构造,枚举则为第⼀个)
repeated表⽰该属性为重复字段,可看作是动态数组,类似于C++中的vector。
如果为optional属性,发送端没有包含该属性,则接收端在解析式采⽤默认值。对于默认值,如果已设置默认值,则采⽤默认值,如果未设置,则类型特定的默认值为使⽤,例如string的默认值为””。
1.3.2 类型
Protobuf的属性基本包含了c++需要的所有基本属性类型。
protobuf属性C++属性java属性备注
double double double固定8个字节
float float float固定4个字节
int32int32int32使⽤变长编码,对于负数编码效率较低,如果经常使⽤负数,建议使⽤sint32
php手册官方中文版int64int64int64使⽤变长编码,对于负数编码效率较低,如果经常使⽤负数,建议使⽤sint64
uint32uint32int使⽤变长编码
uint64uint64long使⽤变长编码
sint32int32int采⽤zigzag压缩,对负数编码效率⽐int32⾼
sint64int64long采⽤zigzag压缩,对负数编码效率⽐int64⾼
fixed32uint32int总是4字节,如果数据>2^28,编码效率⾼于unit32
fixed64uint64long总是8字节,如果数据>2^56,编码效率⾼于unit32
sfixed32int32int总是4字节
sfixed64int64long总是8字节
bool bool boolean
string string String⼀个字符串必须是utf-8编码或者7-bit的ascii编码的⽂本
bytes string ByteString可能包含任意顺序的字节数据
1.3.
2.1 Union类型定义
Protobuf没有提供union类型,如果希望使⽤union类型,可以采⽤enum和optional属性定义的⽅式。
例如,如果已经定义了Foo、Bar、Baz等message,则可以采⽤如下定义。
message OneMessage {
enum Type { FOO = 1; BAR = 2; BAZ = 3; }
/
/ 标识要填写的字段,必填
required Type type = 1;
// 将填写以下内容之⼀,可选
optional Foo foo = 2;
optional Bar bar = 3;
optional Baz baz = 4;
}
1.3.3 属性顺序号
属性顺序号是protobuf为了提⾼数据的压缩和可选性等功能定义的,需要按照顺序进⾏定义,且不允许有重复。
1.4 可选项
1.4.1 import可选项
Import可选项⽤于包含其它proto⽂件中定义的message或enum类型等。标准格式如下
import “phonetype.proto”;
使⽤时,import的⽂件必须与当前⽂件处于同⼀个⽂件夹下,protoc⽆法完成不处于同⼀个⽂件夹下的import选项。
1.4.2 packed
packed (field option): 如果该选项在⼀个整型基本类型上被设置为真,则采⽤更紧凑的编码⽅式。当然使⽤该值并不会对数值造成任何损失。在2.3.0版本之前,解析器将会忽略那些 ⾮期望的包装值。因此,它不可能在不破坏现有框架的兼容性上⽽改变压缩格式。在2.3.0之后,这种改变将是安全的,解析器能够接受上述两种格式,但是在 处理protobuf⽼版本程序时,还是要多留意⼀下。
repeated int32 samples = 4 [packed=true];
1.4.3 default
[default = default_value]: optional类型的字段,如果在序列化时没有被设置,或者是⽼版本的消息中根本不存在该字段,那么在反序列化该类型的消息是,optional的字段将被赋予类型相关的缺省值,如bool被设置为false,int32被设置为0。Protocol Buffer也⽀持⾃定义的缺省值,如:
optional int32 result_per_page = 3 [default = 10]。
1.5 ⼤数据量使⽤建议
在使⽤过程中发现,对于⼤数据量的协议报⽂(循环超过10000条),如果repeated修饰的属性为对象类型(诸如message 、Bytes、string等称为“对象类型”,其余的诸如int32、int64、float等等称为“原始类型”)时,效率⾮常低,⽽且占⽤的进程内存也⾮常⼤,建议采⽤如下⽅式优化。
1.5.1 repeated message类型
在message 中对repeated 标识的 message类型的字段需要做⼤量ADD操作时,可以考虑尽量避免嵌套message或者减少嵌套的message个数。
1.5.2 repeated raw类型
在message中对repeated 标识的原始数据类型的字段需要做⼤量ADD操作(例如超过3千)时,可以考虑预分配数据空间,避免重复⼤量地分配空间。
1.5.3 repeated Bytes类型
在protobuf中,Bytes基于C++ STL中的string实现,因为string内存管理的原因,程序空间往往较⼤。所以应⽤如果有很多repeated Bytes类型的字段的话,进程显⽰耗⽤⼤量内存,这与vector<string>的情况基本⼀致。
1.6 Protocol Buffer消息升级原则
在实际的开发中会存在这样⼀种应⽤场景,既消息格式因为某些需求的变化⽽不得不进⾏必要的升级,但是有些使⽤原有消息格式的应⽤程序暂时⼜不能被⽴刻升级,这便要求我们在升级消息格式时要遵守⼀定的规则,从⽽可以保证基于新⽼消息格式的新⽼程序同时运⾏。规则如下:
1. 不要修改已经存在字段的标签号。
2. 任何新添加的字段必须是optional和repeated限定符,否则⽆法保证新⽼程序在互相传递消息时的消息兼容性。
3. 在原有的消息中,不能移除已经存在的required字段,optional和repeated类型的字段可以被移除,但是他们之前使⽤的标签
号必须被保留,不能被新的字段重⽤。
4. int32、uint32、int64、uint64和bool等类型之间是兼容的,sint32和sint64是兼容的,string和bytes是兼容的,fixed32
和sfixed32,以及fixed64和sfixed64之间是兼容的,这意味着如果想修改原有字段的类型时,为了保证兼容性,只能将其修改为与其原有类型兼容的类型,否则就将打破新⽼消息格式的兼容性。
5. optional和repeated限定符也是相互兼容的。
第2章编译 .proto ⽂件
可以通过定义好的.proto⽂件来⽣成Java、Python、C++代码,需要基于.proto⽂件运⾏protocol buffer编译器protoc。如果你没有安装编译器,下载并遵照README安装。运⾏的命令如下所⽰:
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto
MPORT_PATH声明了⼀个.proto⽂件所在的具体⽬录。如果忽略该值,则使⽤当前⽬录。如果有多个⽬录则可以 对--proto_path 写多次,它们将会顺序的被访问并执⾏导⼊。-I=IMPORT_PATH是它的简化形式。
当然也可以提供⼀个或多个输出路径:
--cpp_out 在⽬标⽬录DST_DIR中产⽣C++代码,可以在中查看更多。
--java_out 在⽬标⽬录DST_DIR中产⽣Java代码,可以在 中查看更多。
--python_out 在⽬标⽬录 DST_DIR 中产⽣Python代码,可以在中查看更多。
--go_out 在⽬标⽬录 DST_DIR 中产⽣Go代码,可以在中查看更多。
--ruby_out在⽬标⽬录 DST_DIR 中产⽣Go代码,参考正在制作中。
--javanano_out在⽬标⽬录DST_DIR中⽣成JavaNano,JavaNano代码⽣成器有⼀系列的选项⽤于定制⾃定义⽣成器的输出:你可以通过⽣成器的查更多信息,JavaNano参考正在制作中。
--objc_out在⽬标⽬录DST_DIR中产⽣Object代码,可以在中查看更多。
--csharp_out在⽬标⽬录DST_DIR中产⽣Object代码,可以在中查看更多。
--php_out在⽬标⽬录DST_DIR中产⽣Object代码,可以在中查看更多。
作为⼀个⽅便的拓展,如果DST_DIR以.zip或者.jar结尾,编译器会将输出写到⼀个ZIP格式⽂件或者符合JAR标准的.jar⽂件中。注意如果输出已经存在则会被覆盖,编译器还没有智能到可以追加⽂件。
- 你必须提议⼀个或多个.proto⽂件作为输⼊,多个.proto⽂件可以只指定⼀次。虽然⽂件路径是相对于当前⽬录的,每个⽂件必须位于其IMPORT_PATH下,以便每个⽂件可以确定其规范的名称。
第3章使⽤message
3.1 类成员变量的访问
在⽣成的.h⽂件中定义了类成员的访问⽅法。例如,对于Person类,定义了name、id、email、phone等成员的访问⽅法。
获取成员变量值直接采⽤使⽤成员变量名(全部为⼩写),设置成员变量值,使⽤在成员变量名前加set_的⽅法。
对于普通成员变量(required和optional)提供has_⽅法判断变量值是否被设置;提供clear_⽅法清除设置的变量值。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论