优质博文:IT-BLOG-CN
一、工具介绍
Kryo 是一个快速高效的 Java 二进制对象图序列化框架。 该项目的目标是高速、小尺寸和易于使用的 API。 该项目在需要持久化对象的任何时候都很有用,无论是文件、数据库还是通过网络。
github地址:https://github.com/EsotericSoftware/kryo
二、使用方法
1 引入kryo工具依赖
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.3.0</version>
</dependency>
2 快速开始
使用下面的代码,可以简单进行对象的深拷贝
public static <T> T kryoCopy(T origin) {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
return kryo.copy(origin);
}
使用下面的代码,可以简单进行对象序列化和反序列化
public static <T> Output kryoSerialize(T origin) {
Kryo kryo = new Kryo();
Output output = new Output();
kryo.setRegistrationRequired(false);
kryo.writeObject(output, origin);
return output;
}
public static <T> T kryoDeserialize(Input input, Class<T> clazz) {
try {
return kryo.readObject(input, clazz);
} catch (Exception e) {
log.error("Deserialize Error", e);
return null;
}
}
三、相关配置
3.1 通用配置
Kryo在拷贝对象前需要先对该类进行注册,包括该对象中使用到的其他类,注册后的对象拷贝速度更快。
kryo.register(T.class);
对于复杂对象,或是其他无法注册完全的对象,可以用以下代码跳过注册
kryo.setRegistrationRequired(false);
Kryo提供了许多序列化器供使用,每个都有各自的作用,具体可以看github上的文档。Kryo使用注册的方式支持对于不同的类使用不同的序列化器。如下所示:
kryo.register(T.class, new FieldSerializer<>(kryo, T.class));
kryo.register(R.class, new TaggedFieldSerializer<>(kryo, R.class));
3.2 FieldSerializer
FieldSerializer对于拷贝的配置如下所示:
配置项 | 描述 | 默认值 |
---|---|---|
copyTransient | 如果为true,那么transient修饰字段也会被拷贝 | true |
可以通过FieldSerializerFactory修改配置,使用方法如下: |
SerializerFactory.FieldSerializerFactory serializerFactory = new SerializerFactory.FieldSerializerFactory();
serializerFactory.getConfig().setCopyTransient(false);
kryo.setDefaultSerializer(serializerFactory);
3.3 TaggedFieldSerializer
TaggedFieldSerializer继承FieldSerializer,对于拷贝无额外配置项。同样可以通过TaggedFieldSerializerFactory修改配置,使用方法如下:
SerializerFactory.TaggedFieldSerializerFactory tagSerializerFactory = new SerializerFactory.TaggedFieldSerializerFactory();
tagSerializerFactory.getConfig().setCopyTransient(false);
kryo.setDefaultSerializer(serializerFactory);
四、并发处理
Kryo提供Pool类用于并发处理,使用过程中需要先创建Kryo池,池的容量表示用于存储的最大对象数,但是同时获取的数量可以超过池的最大容量:
// 构造器参数分别为thread safe, soft references, maximum capacity
Pool<Kryo> KRYO_POOL = new Pool<Kryo>(true, false, 64) {
@Override
protected Kryo create() {
Kryo kryo = new Kryo();
// 配置Kryo
return kryo;
}
};
然后在使用时先从Pool中获取Kryo对象,并在完成拷贝后(无论是否成功)务必归还:
Kryo kryo = KRYO_POOL.obtain();
try {
return kryo.copy(origin);
} catch (Exception e) {
log.error("Deep Copy", e);
throw new RuntimeException("kryo copy fail!", e);
} finally {
// 一定要归还
KRYO_POOL.free(kryo);
}处理序列化对象时,可以创建一个Pool中的泛型类改为Output的池
五、性能测试
深拷贝
1.1 JMH性能对比
分别对Hotel对象(0)、小ListSearchResponse对象(1)、大ListSearchResponse对象(2)进行了多轮深拷贝测试。
JMH配置为单线程、迭代10次取平均值,具体如下所示:
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 10, time = 5)
@Fork(1)
@State(value = Scope.Benchmark)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
每次迭代需要循环1000次多轮拷贝。
测试结果如下图所示,其中testCopy是用ProtostuffSerializer进行序列化,反序列化来深拷贝。从结果可以发现,Kryo的深拷贝时间相较于ProtostuffSerializer平均缩短了50-60%。
1.2 线上性能对比
左图为深拷贝平均耗时(单位毫秒),右图为kryo相较于protostuff的变化幅度,可以发现kryo平均缩短了30-40%的拷贝时间
序列化
测试对象与深拷贝测试中的相同,分别测试了Protostuff序列化、Kryo序列化、以及他们各自加上zstd压缩的序列化性能
2.1 序列化
下图是序列化性能测试结果,可以发现Kryo序列性能更好,对象越大,Kryo优势就更明显
2.2 反序列化
下图是序列化+反序列化性能测试结果,可以发现Kryo序列性能更好,对象越大,Kryo优势就更明显