Protocol Buffer
Protobuf基础概念
Protobuf是google开发的数据结构描述语言,能够将结构化数据序列化与反序列化,取代json和xml,常用于服务器通信协议、RPC系统和数据持久化存储系统中。
优点:高性能,数据协议小,平台无关,语言无关,向前和向后兼容
缺点:通用性比XML差,以二进制存储,无法直接读取出protobuf的内容。
Protobuf在应用场景之一
客户端程序是使用Java开发的,可能运行自不同的平台,如:Linux、Windows或者是Android,而我们的服务器程序通常是基于Linux平台并使用C++开发完成的。在这两种程序之间进行数据通讯时存在多种方式用于设计消息格式。
Protobuf字段类型对应
Protobuf type | java type | c++ type | protobuf type describe |
bool | boolean | bool | |
bytes | ByteString | string | 可包含任意顺序的字节数据。 |
double | double | double | |
float | float | float | |
int32 | int | int32 | 使用可变长编码方式。编码负数时不够高效如果字段负数,那么使用sint32更高效 |
int64 | long | int64 | 使用可变长编码方式。编码负数时不够高效如果字段含有负数,那么使用sint64。 |
uint32 | int | uint32 | 使用可变长编码方式 |
unint64 | long | uint64 | 使用可变长编码方式 |
sint32 | int | int32 | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效 |
sint64 | long | int64 | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效 |
int | uint32 | 总是4个字节。如果数值总是比2的28次方大的话,这个类型会比uint32高效 | |
fixed64 | long | uint64 | 总是8个字节。如果数值总是比256大的话,这个类型会比uint64高效 |
sfixed32 | int | int32 | 总是4个字节 |
sfixed64 | long | int64 | 总是8个字节 |
string | String | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本 |
Protobuf关键字说明
关键字 | .proto语法 | 说明 | ||||
package | package:package_name | 包名定义 | ||||
java_package | java_package:com.example.protobuf | java包路径声明 | ||||
java-outer_classname | Java-outer_classname:classname | 编译后生成的类名的声明 | ||||
message | required | message MessageName{ required string name=1; optional int32 id=2; optional int32 age=3; } | message消息定义关键字,编译后生成实体类,标签1,2,3在每个消息中必须是唯一的 | 该字段必须设置,且消息中至少有一个 | ||
optional | 该字段可以不设置值传递 | |||||
repeated | 该字段可不设置值,java中以List集合表示 | |||||
service | service ServiceName{ rpc RpcName(MessageName)returns(MessageName){} } | RPC服务定义
| ||||
import | import "path/other.proto" | 引入其它的.proto文件 | ||||
enum | enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; } | 表示可为字段指定某“预定义序列值”的一个值,来表示某一类型,如果为 enum修饰的字段指定预定义序列值之外的值,那么解析器就会把它当做未知的值来对待 |
Protobuf在Java中的使用
安装编译器:
到google官网下载编译器(最新版为2.6.1),解压安装编译器,也可在该官网查阅Protocol Buffer API帮助文档,目前只有C++、GO、Java、Python和C#的API,
编译.proto文件
a.安装完编译器后可直接在安装目录的Dos环境下编译或是用批处理方式,为了避免造成不必要的麻烦编译失败,而无法查看错误,建议用批处理方式:
proc path_src=$src_path java_out=$out_path $path\filename.proto
pause
将.proto编译成相应的.java文件
Protobuf各类字段在java中设值/取值方式
属性 字段角色 | 设值方式 | 取值方式 | 是否是必设值 | 有无默认值 |
required | setX() | getX() | 是 | 无 |
optional | setX() | getX() | 否 | 有 |
repeated | addX() | getX(int index)/getX() | 否 | 无 |
Protobuf应用消息升级规则
1) 原有标签号必须被保留,不能被新字段重用。
2)不能移除和添加required限定符字段。
3)新添加字段必须是optional和required限定符。
4)int32、uint32、int64、uint64和bool等类型之间是兼容的,sint32和sint64是兼容的,string和bytes是兼容的且必须是UTF-8编码,fixed32兼容sfixed32,以及fixed64兼容sfixed64,这意味着如果想修改原有字段的类型时,为了保证兼容性,只能将其修改为与其原有类型兼容的类型。
5)optional限定符兼容repeated限定符。
Protobuf和json对比
浏览器端ProtocolBufer和JSON序列化和反序列化对比
| Protocol Buffer | JSON | ||||
数据量 | 序列化所需时间(ms) | 反序列化时间(ms) | 所占空间(B) | 序列化所需时间(ms) | 反序列化时间(ms) | 所占空间(B) |
10 | 5 | 4 | 190 | 0 | 0 | 490 |
100 | 16 | 15 | 1900 | 0 | 0 | 4900 |
1000 | 59 | 51 | 19000 | 0 | 2 | 49000 |
10000 | 294 | 113 | 190000 | 1 | 11 | 490000 |
100000 | 1942 | 744 | 1900000 | 4 | 95 | 4900000 |
1000000 | 28899 | 6974 | 19000000 | 8 | 921 | 49000000 |
Java 服务端ProtocolBuffer和JSON序列化和反序列化对比
| ProtocolBuffer | JSON |
| ||
数据量 | 序列化所需时间 | 反序列化时间 | 序列化所需时间 | 反序列化时间 | |
10 | 30 | 16 | 91 | 40 | |
100 | 33 | 15 | 110 | 51 | |
1000 | 42 | 17 | 211 | 106 | |
10000 | 44 | 113 | 581 | 267 | |
100000 | 48 | 20 | 1014 | 589 | |
1000000 | 68 | 25 | 3663 | 1007 |
Protobuf数据类型编码性能对比
表1
序列化数值 | 数据类型 | 序列化次数 | 序列化所需时间 | 序列化后编码长度 |
-2147483648 | Int32 | 113 | 11 | |
-2147483648 | Sint32 | 100000 | 35 | 6 |
-2147483648 | Uint32 | 100000 | 29 | 6 |
-2147483648 | fixed32 | 100000 | 29 | 5 |
表2
序列化数值 | 序列化次数 | 序列化所需时间 | 序列化后编码长度 | |
2147483647 | Int32 | 100000 | 75 | 6 |
2147483647 | Sint32 | 100000 | 33 | 6 |
2147483647 | Uint32 | 100000 | 28 | 6 |
2147483647 | fixed32 | 100000 | 24 | 5 |
表3
序列化数值 | 数据类型 | 序列化次数 | 序列化所需时间 | 序列化后编码长度 |
134217728 | Int32 | 100000 | 75 | 5 |
134217728 | Sint32 | 100000 | 34 | 6 |
134217728 | Uint32 | 100000 | 28 | 5 |
134217728 | fixed32 | 100000 | 26 | 5 |
条件:
表1,对负值进行序列化
表2,对正直序列化,且该值2147483647大于2的28次方
表3,对正直序列化,且该值134217728小于2的28次方