Protobuf使用手册.docx
- 文档编号:18311787
- 上传时间:2023-08-15
- 格式:DOCX
- 页数:46
- 大小:61.35KB
Protobuf使用手册.docx
《Protobuf使用手册.docx》由会员分享,可在线阅读,更多相关《Protobuf使用手册.docx(46页珍藏版)》请在冰点文库上搜索。
Protobuf使用手册
Protobuf使用手册
第1章定义.proto文件
首先我们需要编写一个proto文件,定义我们程序中需要处理的结构化数据,在protobuf的术语中,结构化数据被称为Message。
proto文件非常类似java或者C语言的数据定义,可以使用C或C++风格的注释。
下面是一个proto文件的例子。
packagetutorial;
optionjava_package="com.example.tutorial";
optionjava_outer_classname="AddressBookProtos";
messagePerson{
requiredstringname=1;
requiredint32id=2;//UniqueIDnumberforthisperson.
optionalstringemail=3;
enumPhoneType{
MOBILE=0;
HOME=1;
WORK=2;
}
messagePhoneNumber{
requiredstringnumber=1;
optionalPhoneTypetype=2[default=HOME];
}
repeatedPhoneNumberphone=4;
}
//Ouraddressbookjustoneofthese.
messageAddressBook{
repeatedPersonperson=1;
}
一个proto文件主要包含package定义、message定义和属性定义三个部分,还有一些可选项。
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个字节
int32
int32
int32
使用变长编码,对于负数编码效率较低,如果经常使用负数,建议使用sint32
int64
int64
int64
使用变长编码,对于负数编码效率较低,如果经常使用负数,建议使用sint64
uint32
uint32
int
使用变长编码
uint64
uint64
long
使用变长编码
sint32
int32
int
采用zigzag压缩,对负数编码效率比int32高
sint64
int64
long
采用zigzag压缩,对负数编码效率比int64高
fixed32
uint32
int
总是4字节,如果数据>2^28,编码效率高于unit32
fixed64
uint64
long
总是8字节,如果数据>2^56,编码效率高于unit32
sfixed32
int32
int
总是4字节
sfixed64
int64
long
总是8字节
bool
bool
boolean
string
string
String
一个字符串必须是utf-8编码或者7-bit的ascii编码的文本
bytes
string
ByteString
可能包含任意顺序的字节数据
1.3.2.1Union类型定义
Protobuf没有提供union类型,如果希望使用union类型,可以采用enum和optional属性定义的方式。
例如,如果已经定义了Foo、Bar、Baz等message,则可以采用如下定义。
messageOneMessage{
enumType{FOO=1;BAR=2;BAZ=3;}
//Identifieswhichfieldisfilledin.
requiredTypetype=1;
//Oneofthefollowingwillbefilledin.
optionalFoofoo=2;
optionalBarbar=3;
optionalBazbaz=4;
}
1.3.3属性顺序号
属性顺序号是protobuf为了提高数据的压缩和可选性等功能定义的,需要按照顺序进行定义,且不允许有重复。
1.4其它可选项
ProtocolBuffer允许我们在.proto文件中定义一些常用的选项,这样可以指示ProtocolBuffer编译器帮助我们生成更为匹配的目标语言代码。
ProtocolBuffer内置的选项被分为以下三个级别:
1.文件级别,这样的选项将影响当前文件中定义的所有消息和枚举。
2.消息级别,这样的选项仅影响某个消息及其包含的所有字段。
3.字段级别,这样的选项仅仅响应及其相关的字段。
1.4.1java_package可选项
java_package():
是文件级别的选项,表明生成java类所在的包。
如果在.proto文件中没有明确的声明java_package,就采用默认的包名。
当然了,默认方式产生的java包名并不是最好的方式,按照应用名称倒序方式进行排序的。
如果不需要产生java代码,则该选项将不起任何作用。
及此同时,生成的Java文件也将会自动存放到指定输出目录下的com/example/foo子目录中。
如:
optionjava_package="com.example.foo";
1.4.2java_outer_classname可选项
java_outer_classname():
是文件级别的选项,表明想要生成Java类的名称。
如果在.proto文件中没有明确的java_outer_classname定义,生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。
如(foo_bar.proto生成的java类名为FooBar.java),如果不生成java代码,则该选项不起任何作用。
如:
optionjava_outer_classname="Ponycopter";
注:
主要是因为Java中要求同一个.java文件中只能包含一个Java外部类或外部接口,而C++则不存在此限制。
因此在.proto文件中定义的消息均为指定外部类的内部类,这样才能将这些消息生成到同一个Java文件中。
在实际的使用中,为了避免总是输入该外部类限定符,可以将该外部类静态引入到当前Java文件中,如:
importstaticpany.project.LYPhoneMessage.*。
1.4.3*_generic_services可选项
cc_generic_services,java_generic_services,py_generic_services():
在C++、java、python中protocolbuffer编译器是否应该基于服务定义产生抽象服务代码。
由于历史遗留问题,该值默认是true。
但是自2.3.0版本以来,它被认为通过提供代码生成器插件来对RPC实现更可取,而不是依赖于“抽象”服务。
//Thisonpluginstogenerateservicecode.
optioncc_generic_services=false;
optionjava_generic_services=false;
optionpy_generic_services=false;
1.4.4message_set_wire_format可选项
message_set_wire_format(messageoption):
如果该值被设置为true,该消息将使用一种不同的二进制格式来及Google内部的MessageSet的老格式相兼容。
对于Google外部的用户来说,该选项将不会被用到。
如下所示:
messageFoo{
optionmessage_set_wire_format=true;
extensions4tomax;
}
1.4.5import可选项
Import可选项用于包含其它proto文件中定义的message或enum类型等。
标准格式如下
import“phonetype.proto”;
使用时,import的文件必须及当前文件处于同一个文件夹下,protoc无法完成不处于同一个文件夹下的import选项。
1.4.6optimize_for
optimize_for():
是文件级别的选项,可以被设置为SPEED,CODE_SIZE,orLITE_RUNTIME,缺省情况下是SPEED。
这些值将通过如下的方式影响C++及java代码的生成:
SPEED(default):
protocolbuffer编译器将通过在消息类型上执行序列化、语法分析及其他通用的操作。
这种代码是最优的。
CODE_SIZE:
protocolbuffer编译器将会产生最少量的类,通过共享或基于反射的代码来实现序列化、语法分析及各种其它操作。
采用该方式产生的代码将比SPEED要少得多,但是操作要相对慢些。
当然实现的类及其对外的API及SPEED模式都是一样的。
这种方式经常用在一些包含大量的.proto文件而且并不盲目追求速度的应用中。
LITE_RUNTIME:
protocolbuffer编译器依赖于运行时核心类库来生成代码(即采用libprotobuf-lite替代libprotobuf)。
这种核心类库由于忽略了一些描述符及反射,要比全类库小得多。
这种模式经常在移动手机平台应用多一些。
编译器采用该模式产生的方法实现及SPEED模式不相上下,产生的类通过实现MessageLite接口,但它仅仅是Messager接口的一个子集。
optionoptimize_for=CODE_SIZE;
1.4.7packed
packed(fieldoption):
如果该选项在一个整型基本类型上被设置为真,则采用更紧凑的编码方式。
当然使用该值并不会对数值造成任何损失。
在2.3.0版本之前,解析器将会忽略那些非期望的包装值。
因此,它不可能在不破坏现有框架的兼容性上而改变压缩格式。
在2.3.0之后,这种改变将是安全的,解析器能够接受上述两种格式,但是在处理protobuf老版本程序时,还是要多留意一下。
repeatedint32samples=4[packed=true];
1.4.8default
[default=default_value]:
optional类型的字段,如果在序列化时没有被设置,或者是老版本的消息中根本不存在该字段,那么在反序列化该类型的消息是,optional的字段将被赋予类型相关的缺省值,如bool被设置为false,int32被设置为0。
ProtocolBuffer也支持自定义的缺省值,如:
optionalint32result_per_page=3[default=10]。
1.5大数据量使用建议
在使用过程中发现,对于大数据量的协议报文(循环超过10000条),如果repeated修饰的属性为对象类型(诸如message、Bytes、string等称为“对象类型”,其余的诸如int32、int64、float等等称为“原始类型”)时,效率非常低,而且占用的进程内存也非常大,建议采用如下方式优化。
1.5.1repeatedmessage类型
在message中对repeated标识的message类型的字段需要做大量ADD操作时,可以考虑尽量避免嵌套message或者减少嵌套的message个数。
实例如下所示:
->
按行存取
按列存取
1.5.2repeatedraw类型
在message中对repeated标识的原始数据类型的字段需要做大量ADD操作(例如超过3千)时,可以考虑预分配数据空间,避免重复大量地分配空间。
实例如下所示:
->
未预分配空间
预分配空间
1.5.3repeatedBytes类型
在protobuf中,Bytes基于C++STL中的string实现,因为string内存管理的原因,程序空间往往较大。
所以应用如果有很多repeatedBytes类型的字段的话,进程显示耗用大量内存,这及vector
1.6ProtocolBuffer消息升级原则
在实际的开发中会存在这样一种应用场景,既消息格式因为某些需求的变化而不得不进行必要的升级,但是有些使用原有消息格式的应用程序暂时又不能被立刻升级,这便要求我们在升级消息格式时要遵守一定的规则,从而可以保证基于新老消息格式的新老程序同时运行。
规则如下:
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文件运行protocolbuffer编译器protoc。
运行的命令如下所示:
protoc--proto_path=IMPORT_PATH--cpp_out=DST_DIR--java_out=DST_DIR--python_out=DST_DIRpath/to/
MPORT_PATH声明了一个.proto文件所在的具体目录。
如果忽略该值,则使用当前目录。
如果有多个目录则可以对--proto_path写多次,它们将会顺序的被访问并执行导入。
-I=IMPORT_PATH是它的简化形式。
当然也可以提供一个或多个输出路径:
--cpp_out在目标目录DST_DIR中产生C++代码,可以在/cpp-generated.html中查看更多。
--java_out在目标目录DST_DIR中产生Java代码,可以在/java-generated.html中查看更多。
--python_out在目标目录DST_DIR中产生Python代码,可以在/docs/reference/python-generated.html中查看更多。
作为一种额外的使得,如果DST_DIR是以.zip或.jar结尾的,编译器将输出结果打包成一个zip格式的归档文件。
.jar将会输出一个JavaJAR声明必须的manifest文件。
注:
如果该输出归档文件已经存在,它将会被重写,编译器并没有做到足够的智能来为已经存在的归档文件添加新的文件。
你必须提供一个或多个.proto文件作为输入。
多个.proto文件能够一次全部声明。
虽然这些文件是相对于当前目录来命名的,每个文件必须在一个IMPORT_PATH中,只有如此编译器才可以决定它的标准名称。
第3章使用message
3.1类成员变量的访问
在生成的.h文件中定义了类成员的访问方法。
例如,对于Person类,定义了name、id、email、phone等成员的访问方法。
获取成员变量值直接采用使用成员变量名(全部为小写),设置成员变量值,使用在成员变量名前加set_的方法。
对于普通成员变量(required和optional)提供has_方法判断变量值是否被设置;提供clear_方法清除设置的变量值。
对于string类型,提供多种set_方法,其参数不同。
同时,提供了一个mutable_方法,返回变量值的可修改指针。
对于repeated变量,提供了其它一些特殊的方法:
●_size方法:
返回repeatedfield’s
●通过下脚标访问其中的数组成员组
●通过下脚标返回其中的成员的mutable_的方法
●_add方法:
增加一个成员。
//name
inlineboolhas_name()const;
inlinevoidclear_name();
inlineconst:
:
std:
:
string&name()const;
inlinevoidset_name(const:
:
std:
:
string&value);
inlinevoidset_name(constchar*value);
inline:
:
std:
:
string*mutable_name();
//id
inlineboolhas_id()const;
inlinevoidclear_id();
inlineint32_tid()const;
inlinevoidset_id(int32_tvalue);
inlineboolhas_email()const;
inlinevoidclear_email();
inlineconst:
:
std:
:
string&email()const;
inlinevoidset_email(const:
:
std:
:
string&value);
inlinevoidset_email(constchar*value);
inline:
:
std:
:
string*mutable_email();
//phone
inlineintphone_size()const;
inlinevoidclear_phone();
inlineconst:
:
google:
:
protobuf:
:
RepeatedPtrField<:
:
tutorial:
:
Person_PhoneNumber>&phone()const;
inline:
:
google:
:
protobuf:
:
RepeatedPtrField<:
:
tutorial:
:
Person_PhoneNumber>*mutable_phone();
inlineconst:
:
tutorial:
:
Person_PhoneNumber&phone(intindex)const;
inline:
:
tutorial:
:
Person_PhoneNumber*mutable_phone(intindex);
inline:
:
tutorial:
:
Person_PhoneNumber*add_phone();
3.2标准message方法
生成的.h文件中的class都继承自:
:
google:
:
protobuf:
:
Message类,Message类提供了一些方法可以检查或者操作整个message,包括
●boolIsInitialized()const;检查是否所有required变量都已经初始化;
●stringDebugString()const;返回message的可阅读的表示,主要用于调试程序;
●voidCopyFrom(constPerson&from);使用一个message的值覆盖本message;
●voidClear();清空message的所有成员变量值。
3.3编码和解码函数
每个message类都提供了写入和读取message数据的方法,包括
●boolSerializeToString(string*output)const;把message编码进output。
●boolParseFromString(conststring&data);从string解码到message
●boolSerializeToArray(char*buf,intsize)const;把message编码进数组buf.
●boolParseFromArray(constchar*buf,intsize);把buf解码到message。
此解码方法效率较ParseFromString高很多,所以一般用这种方法解码。
●boolSerializeToOstream(ostream*output)const;把message编码进ostream
●boolParseFromIstream(istream*input);从istream解码到message
备注:
发送接收端所使用的加码解码方法不一定非得配对,即发送端用SerializeToString接收端不一定非得用ParseFromString,可以使用其他解码方法。
3.4简单message生成的C
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Protobuf 使用手册