三、Protobuf序列化
介绍
protobuf—Github地址、protobuf-java
介绍:Protobuf 出自于 Google,性能还比较优秀,也支持多种语言,同时还是跨平台的。就是在使用中过于繁琐,因为你需要自己定义 IDL 文件和生成对应的序列化代码。这样虽然不然灵活,但是,另一方面导致 protobuf 没有序列化漏洞的风险。
Protobuf 包含序列化格式的定义、各种语言的库以及一个 IDL 编译器。正常情况下你需要定义 proto 文件,然后使用 IDL 编译器编译成你需要的语言。
正常流程:定义proto文件 -> 使用proto编译工具编译得到Java类 -> 使用该类来进行序列化与反序列化。
快速开始
引入依赖
<!-- protobuf --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.21.1</version> </dependency>
使用步骤
1、首先安装proto的转换工具
下载地址
安装配置好对应的path路径,测试下命令:
protoc --version
安装这个proto工具的目的是将对应自己编写的.proto文件转为一个Java类,使用这个Java类即可进行序列化与反序列化。
在一个.proto文件中可以写多个结构体都是可以的。
2、编写proto文件
使用ProtoBuf序列化数据—可查看对应protobuf对应java的类型
syntax = "proto3"; option java_package = "com.changlu.serialize.protobuf"; option java_outer_classname = "DemoModel"; message User { string name = 1; uint32 age = 2; } message Message { uint32 message_type = 1; }
java_package:表示目标生成的包名路径。
java_outer_classname:目标生成的工具类名称。
说明:若是觉得自己编写比较麻烦,那么我们可以自己先定义Java实体类,然后使用IDEA的插件将这个实体类转为对应proto的struct类型。
插件名称:pojo to proto;protobuf插件安装使用
3、准备好proto文件了之后,我们就要开始生成对应的工具类了
当前路径在main/proto/xxx下,我们想要输出到对应的main/java/com/changlu/serialize/protobuf/xxx中:
接着我们输入命令:
# -I:我自己编写的.proto 文件的位置。 # --java out 输出位置会以完整包名的形式出输出,我指定的是上级目录的java中,此时配合proto文件里的com.changlu.xxx,即可输出到我想要的目录下 # ./subscribeReq.proto:当前需要编译成java的proto文件名。 protoc -I=./ --java_out=../java ./User.proto
ok,此时我们就有了这个工具类DemoModel,对应的User、Message的实体类都在这个DemoModel类中有了,对于序列化与反序列化操作也在这个DemoModel中进行。
测试
//测试ProtoBuf testProtobufSerialzize();
public void testProtobufSerialzize(){ //准备实体类 DemoModel.User.Builder userBuilder = DemoModel.User.newBuilder(); userBuilder.setAge(18); userBuilder.setName("changlu"); DemoModel.User user = userBuilder.build(); System.out.println("=====开始序列化:Protobuf====="); System.out.println("开始进行序列化"); long startTime = System.nanoTime(); //序列化 byte[] data = user.toByteArray(); long endTime = System.nanoTime(); System.out.println(" 序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒"); System.out.println(" 序列化后的内容为:" + new String(data)); System.out.println(" 序列化后的长度为:" + data.length); System.out.println("开始进行反序列化"); startTime = System.nanoTime(); //反序列化 try { System.out.println(" 反序列化后得到的对象为:" + DemoModel.User.parseFrom(data)); } catch (InvalidProtocolBufferException e) { throw new RuntimeException("Serialization failed"); } endTime = System.nanoTime(); System.out.println(" 反序列化时间为:" + (endTime - startTime) / 1000000000.0 + "秒"); System.out.println("=====结束序列化:Protobuf=====" + "\n"); }
四、ProtoStuff
介绍
protostuff-github
由于 Protobuf 的易用性,它的哥哥 Protostuff 诞生了。
protostuff 基于 Google protobuf,但是提供了更多的功能和更简易的用法。虽然更加易用,但是不代表 ProtoStuff 性能更差。
快速开始 <!-- protobufstuff --> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.7.4</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.7.4</version> </dependency> <!-- protobufstuff -->
package com.changlu.serialize; import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; /** * @Description: ProtoStuffSer序列化工具 * @Author: changlu * @Date: 11:33 AM */ public class ProtoStuffSerializer implements Serializer{ //DEFAULT_BUFFER_SIZE:512 //每次序列化时使用缓冲区 private static final LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); public byte[] serialize(Object obj) { Class<?> clazz = obj.getClass(); Schema schema = RuntimeSchema.getSchema(clazz); byte[] bytes; try { //序列化 bytes = ProtostuffIOUtil.toByteArray(obj, schema, BUFFER); }finally { BUFFER.clear(); } return bytes; } public <T> T deserialize(byte[] bytes, Class<T> clazz) { Schema<T> schema = RuntimeSchema.getSchema(clazz); //反序列化 T obj = schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes, obj, schema); return obj; } }
测试
//测试ProtoStuff testSerialize(new ProtoStuffSerializer(), rpcResponse);
说明:这个序列化算法的反序列化是最快的目前来看。
五、hessian
介绍
hessian 是一个轻量级的,自定义描述的二进制 RPC 协议。hessian 是一个比较老的序列化实现了,并且同样也是跨语言的。
dubbo RPC 默认启用的序列化方式是 hessian2 ,但是,Dubbo 对 hessian2 进行了修改,不过大体结构还是差不多。
快速开始 <!-- hessian --> <dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.65</version> </dependency>
package com.changlu.serialize; import com.caucho.hessian.io.HessianInput; import com.caucho.hessian.io.HessianOutput; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * @Description: Hessian序列化 * @Author: changlu * @Date: 12:38 PM */ public class HessianSerializer implements Serializer{ @Override public byte[] serialize(Object obj) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream();){ HessianOutput hessianOutput = new HessianOutput(baos); //序列化 hessianOutput.writeObject(obj); return baos.toByteArray(); } catch (IOException e) { throw new RuntimeException("Serialization failed"); } } @Override public <T> T deserialize(byte[] bytes, Class<T> clazz) { try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);){ HessianInput hessianInput = new HessianInput(bais); //反序列化 return clazz.cast(hessianInput.readObject(clazz)); } catch (IOException e) { throw new RuntimeException("Serialization failed"); } } }
测试
//测试Hession testSerialize(new HessianSerializer(), rpcResponse);
总结
Kryo 是专门针对 Java 语言序列化方式并且性能非常好,如果你的应用是专门针对 Java 语言的话可以考虑使用,并且 Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。
其他跨语言的序列化方式包含:Protobuf、 ProtoStuff、hessian如果有跨语言需求的话可以考虑使用,其他还包含Thrift,Avro 这些。