配色: 字号:
Protobuf算法基础
2022-09-15 | 阅:  转:  |  分享 
  
Protobuf算法基础2018-04-08版本修改人修改记录修改时间V1.0王守奎编写2018/4/08目录protobuf简介4已经学习
过的串行化系统4适用场景4Protobuf特点4Protobuf安装5Linux安装5Window安装6.proto文件6?Pro
tobuf消息定义7注意9体验protobuf101、设计对象102、描述对象103、编译描述文件114、创建maven项目11
5、代码实现12protobuf简介protocolbuffer(以下简称PB)是google的一种数据交换的格式,它独立于语言
,独立于平台。google提供了多种语言的实现:java、c#、c++、go和?python,每一种实现都包含了相应语言的编译
器以及库文件。由于它是一种二进制的格式,比使用?xml?进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数
据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。已经学习过的串行化系
统java串行化//ObjectOutputStream/ObjectInputStreamHadoopWritable串行化
//DataOutputStream/DataInputStreamAvro串行化//DataFileWriter/Da
taFileReader适用场景跨平台的RPC数据传输。相比json的解析要更快,数据量更小。而且数据结构明朗。Protobuf
特点Google定义的一套数据协议,用于数据的结构化和序列化。Google绝大部分模块数据交互基于此数据协议。1、平台无关、语言
无关。2、二进制、数据自描述。3、提供了完整详细的操作API。4、高性能比xml要快20-100倍5、尺寸小比xml要小3-1
0倍–高可扩展性6、数据自描述、前后兼容适用于1、不同的平台、系统、语言、模块之间高效的数据交互2、用于构建大型的复杂系统,降低
数据层面的耦合度和复杂度这里要特别着重说的是protocolBuffer是一种数据协议,就像tcp/ip协议一样,只要是遵守此协议
的任何系统之间都能高效的进行数据交互。第二个特别要说的是?数据自描述。也就是说拿到任何一个protocolBuffer的数据文件
,我们不需要任何其他的辅助信息,就能顺利的解析出其中的数据信息。这2点是最本质的。自带了一个编译器(protoc),只需要用它进行
编译,可以编译成Java、python、C++、C#、Go等代码,然后就可以直接使用,不需要再写其他代码,自带有解析的代码Prot
obuf安装Linux安装下载protobuf代码?https://github.com/protocolbuffers/prot
obuf2、安装protobuftar-xvfprotobufcdprotobuf./configure--pref
ix=/usr/local/protobufmakemakecheckmakeinstall至此安装完成^_^,下面是配
置:(1)vim/etc/profile,添加exportPATH=$PATH:/usr/local/protobuf/b
in/exportPKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/保存执
行,source/etc/profile。同时在~/.profile中添加上面两行代码,否则会出现登录用户找不到protoc命令
。(2)配置动态链接库vim/etc/ld.so.conf,在文件中添加/usr/local/protobuf/lib(注意
:在新行处添加),然后执行命令:ldconfigWindow安装0、准备工作a、下载protoc-2.5.0-win32.zi
pb、解压c、配置环境变量PROTO_HOME=C:\path\to\protocPath=...;%PROTO_HOME%.p
roto文件.proto文件是protobuf一个重要的文件,它定义了需要序列化数据的结构。使用protobuf的3个步骤是:1
在.proto文件中定义消息格式2用protobuf编译器编译.proto文件3用C++/Java等对应的protobuf
API来写或者读消息protobuf文件:就是定义你要的消息(类似java中的类)和消息中的各个字段及其数据类型(类似java类中
的成员变量和他的数据类型)?Protobuf消息定义消息由至少一个字段组合而成,类似于C语言中的结构。每个字段都有一定的格式。字段
格式:限定修饰符①|数据类型②|字段名称③|=|字段编码值④|[字段默认值⑤]1)限定修饰符包含requir
ed\optional\repeatedRequired:表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值
,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导
致消息被丢弃。Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收
方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特
性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通
信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。Repeated:表示该字段可以
包含[0,N]个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。?2)数据类型?Pro
tobuf定义了一套基本数据类型。几乎都可以映射到C++\Java等语言的基础数据类型.N表示打包的字节并不是固定。而是根据数据
的大小或者长度。例如int32,如果数值比较小,在0~127时,使用一个字节打包。关于枚举的打包方式和uint32相同。关于mes
sage,类似于C语言中的结构包含另外一个结构作为数据成员一样。关于fixed32和int32的区别。fixed32的打包效率
比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择f
ixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.3)字段名称字段名称的命名与C、C++、Java等语言的
变量命名方式几乎是相同的。protobuf建议字段的命名采用以下划线分割的驼峰式。例如first_name而不是firstNa
me.4)字段编码值有了该值,通信双方才能互相识别对方的字段。当然相同的编码值,其限定修饰符和数据类型必须相同。编码值的取值范围为
1~2^32(4294967296)。其中1~15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低(相
对于1-15),当然一般情况下相邻的2个值编码效率的是相同的,除非2个值恰好实在4字节,12字节,20字节等的临界区。比如15和1
6.1900~2000编码值为Googleprotobuf系统内部保留值,建议不要在自己的项目中使用。protobuf还建议
把经常要传递的值把其字段编码设置为1-15之间的值。消息中的字段的编码值无需连续,只要是合法的,并且不能在同一个消息中有字段包含相
同的编码值。建议:项目投入运营以后涉及到版本升级时的新增消息字段全部使用optional或者repeated,尽量不实用requi
red。如果使用了required,需要全网统一升级,如果使用optional或者repeated可以平滑升级。5)默认值。当在传
递数据时,对于required数据类型,如果用户没有设置值,则使用默认值传递到对端。当接受数据是,对于optional字段,如果没
有接收到optional字段,则设置为默认值。注意1)关于importprotobuf接口文件可以像C语言的h文件一个,分离为
多个,在需要的时候通过import导入需要对文件。其行为和C语言的#include或者java的import的行为大致相同。2)
关于package避免名称冲突,可以给每个文件指定一个package名称,对于java解析为java中的包。对于C++则解析为名称
空间。对于Java,包声明符会变为java的一个包,除非在.proto文件中提供了一个明确有java_package。3)关于me
ssage支持嵌套消息,消息可以包含另一个消息作为其字段。也可以在消息内定义一个新的消息。Message在C++中对应class。
Message中定义的全部属性在class中全部为private的。Message的嵌套使用可以嵌套定义,也可以采用先定义再使用的
方式。Message的定义末尾可以采用java方式在不加“;”,也可以采用C++定义方式在末尾加上“;”,这两种方式都兼容,建议采
用java定义方式。向.proto文件添加注释,可以使用C/C++/java风格的双斜杠(//)?语法格式。关于enum枚举的定义
和C++相同,但是有一些限制。枚举值必须大于等于0的整数。使用分号(;)分隔枚举变量而不是C++语言中的逗号(,)eg.enum
VoipProtocol?{H323=1;SIP?=2;MGCP=3;H248=4;}体验protobuf
1、设计对象[按照业务需要进行设计]描述对象采用proto文本文件描述[addressbook.proto]packagetut
orial;optionjava_package="com.bm.protobuf";optionjava_outer_c
lassname="AddressBookProtos";messagePerson{requiredstringnam
e=1;requiredint32id=2;optionalstringemail=3;enumPhoneT
ype{MOBILE=0;HOME=1;WORK=2;}messagePhoneNumber{requiredst
ringnumber=1;optionalPhoneTypetype=2[default=HOME];}repe
atedPhoneNumberphone=4;}messageAddressBook{repeatedPersonp
erson=1;}3、编译描述文件protoc--java_out源码生成路径.proto文件路径生成com.bm.pr
otobuf.AddressBookProtos.java4、创建maven项目导入坐标,复制生成的类到项目中[pom.xml]<
projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http
://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://
maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.
0.xsd">4.0.0com.bm>protobuf0.0.1-SNAPSHOTn>jarprotobufhttp://mave
n.apache.org
UTF-8
ency>junitjuniton>3.8.1
testoupId>com.google.protobuf
protobuf-javaifactId>2.5.0ject>5、代码实现串行化代码Serializepackagecom.bm.protobuf;importjava.io.F
ileOutputStream;importjava.io.IOException;importjava.io.ObjectO
utputStream;importcom.bm.protobuf.AddressBookProtos.Person;impor
tcom.bm.protobuf.AddressBookProtos.Person.Builder;importcom.bm.
protobuf.AddressBookProtos.Person.PhoneNumber;importcom.bm.proto
buf.AddressBookProtos.Person.PhoneType;/Copyright(C)2018编
码界的小菜鸟作者:王守奎2018年10月31日下午4:13:01/publicclassSerial{public
staticvoidmain(String[]args)throwsIOException{Builderbuil
der=Person.newBuilder();builder.setId(100);builder.setName("chi
naMobile");builder.setEmail("mobile@139.com");PhoneNumberphoneNu
m=PhoneNumber.newBuilder().setNumber("13111111112").setType(Pho
neType.HOME).build();//builder.setPhone(0,phoneNum);builder.add
Phone(phoneNum);Personp=builder.build();//javaserializelong
start=System.nanoTime();ObjectOutputStreamoos=newObjectOutp
utStream(newFileOutputStream("d:/protobuf/java.dat"));oos.writeO
bject(p);oos.close();longend=System.nanoTime();longdur1=end
-start;System.out.println(dur1);//googleserializestart=Syst
em.nanoTime();FileOutputStreamfos=newFileOutputStream("d:/pro
tobuf/google.dat");p.writeTo(fos);fos.close();end=System.nanoTi
me();longdur2=end-start;System.out.println(dur2);//多条数据使用w
riteDelimitedTo实现FileOutputStreamfos1=newFileOutputStream("d:
/protobuf/google1.dat");Builderbuilder1=Person.newBuilder();bu
ilder1.setId(100);builder1.setName("chinaMobile1");builder1.setEm
ail("mobile@139.com");builder1.addPhone(phoneNum);Personp1=bui
lder1.build();p.writeDelimitedTo(fos1);p1.writeDelimitedTo(fos1);
fos1.close();}}反串行化代码Deserializepackagecom.bm.protobuf;importja
va.io.FileInputStream;importjava.io.IOException;importjava.io.O
bjectInputStream;importcom.bm.protobuf.AddressBookProtos.Person;
/Copyright(C)2018编码界的小菜鸟作者:王守奎2018年10月31日下午4:13:28/p
ublicclassDeSerial{publicstaticvoidmain(String[]args)thro
wsIOException,ClassNotFoundException{//javadeSerializelongs
tart=System.nanoTime();ObjectInputStreamoos=newObjectInputS
tream(newFileInputStream("d:/protobuf/java.dat"));Personp=(Pe
rson)oos.readObject();oos.close();longend=System.nanoTime();l
ongdur1=end-start;System.out.println(dur1);System.out.printl
n(p.getEmail());//googledeSerializestart=System.nanoTime();FileInputStreamfos=newFileInputStream("d:/protobuf/google.dat");Personperson=Person.parseFrom(fos);fos.close();end=System.nanoTime();longdur2=end-start;System.out.println(dur2);System.out.println(person.getEmail());System.out.println(dur1/dur2);//多条记录使用parseDelimitedFrom获取FileInputStreamfos1=newFileInputStream("d:/protobuf/google1.dat");Personper=null;while((per=Person.parseDelimitedFrom(fos1))!=null){System.out.println(per.getName());}}}魁魁语录:加班4A项目组4A项目组
献花(0)
+1
(本文系王守奎的图...原创)