Protobuf是谷歌推出的一款平台无关的序列化协议,相比传统的序列化方式,Protobuf体积更小,更灵活,能有效提高传输效率,减少数据传输过程中占用的带宽。简单来说,Protobuf有以下特点:
语言无关、平台无关。Protobuf支持Java、C++、Python等多种语言,多个平台
高效。比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序
本文以Animal实体类为例,感受protobuf的特点;
public class Animal implements Serializable {
private Long id;
private String name;
private List<String> actions;
/**省略get、set方法**/
}
一、使用Protobuf
创建AnimalProto.proto,定义的数据类型如下:
package com.keduw.protobuf;
message Animal{
//id
required int64 id = 1;
//name
optional string name = 2;
//actions
repeated string actions = 3;
}
Protobuf定义了它自己的语法规则。package定义我们最后生成Java代码所属的包,required表示该字段是必填的,optional表示该字段是可选的,而repeated则标明该字段是可连续的,对应到Java中则表示一个集合,通过定义字段的顺序方便Protobuf对字段的反序列化。具体对应的数据类型可以参考如下:
编写好AnimalProto.proto,可以通过官网提供的protoc.exe生成对应的Java代码(如果找不到可在文章末尾下载)。
二、序列化和反序列化
前面通过工具生成的代码(AnimalProto)已经帮我们封装好了序列化和反序列化的方法,我们只需要调用对应方法即可。
引入Protobuf的依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.4.1</version>
</dependency>
序列化:
/**
* 调用对象构造好的Builder,完成属性赋值和序列化操作
* @return
*/
public static byte[] protobufSerializer(){
AnimalProto.Animal.Builder builder = AnimalProto.Animal.newBuilder();
builder.setId(1L);
builder.setName("小猪");
List<String> actions = new ArrayList<>();
actions.add("eat");
actions.add("run");
builder.addAllActions(actions);
return builder.build().toByteArray();
}
反序列化:
/**
* 通过调用parseFrom则完成反序列化
* @param bytes
* @return
* @throws InvalidProtocolBufferException
*/
public static Animal deserialize(byte[] bytes) throws Exception {
AnimalProto.Animal pAnimal = AnimalProto.Animal.parseFrom(bytes);
Animal animal = new Animal();
animal.setId(pAnimal.getId());
animal.setName(pAnimal.getName());
animal.setActions(pAnimal.getActionsList());
return animal;
}
测试:
public static void main(String[] args) throws Exception {
byte[] bytes = serializer();
Animal animal = deserialize(bytes);
System.out.println(animal);
}
可以看到是能正常序列化和反序列化的。
三、性能对比
另外,我在这里还单独对比了Java原生序列化、jackson和Protobuf序列化对同一个数据Animal操作后的数据长度,可以看到Protobuf序列化后的长度是明显比较小,可见Protobuf的性能还是很优秀的。
至于Protobuf为什么能做到这些提高,一方面通过定义字段顺序完成数据映射,因此不用保存字段名,另一方面,这跟他自己定义的数据结构也有关系,对于不同的数据类型采用了不同的序列化方式,对实现原理感兴趣的可以自行了解。
本文源代码以及*.proto文件的转换工具:【下载】