序列化 — Kryo序列化
一.Kryo介绍
Kryo是一个快速且高效的针对Java对象序列化的框架。它的特点:
- 序列化的性能非常高
- 序列化结果体积较小
- 提供了简单易用的API
Kryo序列化被很多开源项目使用,社区非常活跃,版本迭代也比较快。以下的重大项目中都在使用Kryo
- Apache Hive
- Apache Spark
- Twitter's Chill
- Storm
- akka-kryo-serialization
由此可见Kryo的确具有很大的优势。但是Kryo是针对Java Object的序列化,对于跨语言方面是不支持的,但是很多场景中比如RPC,Cache,Store场景中一般很少需要对跨语言的支持。因此,Kryo的适用场景也很不错。
二.Kryo使用
static void quickStart() throws FileNotFoundException {
Kryo kryo = new Kryo();
Output output = new Output(new FileOutputStream("file.bin"));
SomeClass someObject = new SomeClass();
someObject.setValue("this is someObject.");
kryo.writeObject(output, someObject);
output.close();
Input input = new Input(new FileInputStream("file.bin"));
SomeClass deSomeObject = kryo.readObject(input, SomeClass.class);
input.close();
Assert.assertEquals(someObject.getValue(), deSomeObject.getValue());
}
1.Kryo的IO
Kryo致力以简单易用的API,序列化过程中主要核心有Kryo、Output、Input。
Output和Input是Kryo的IO,他们支持以byte array或者stream的形式为序列化的dest和反序列化的source。当使用stream形式进行写出写入时,需要close这些Output和Input。
写出时,当OutputDe buffer是满的时候,就会flush bytes到stream中。写入时,会从stream中获取bytes到Input buffer中,当填充满时,进行反序列化。
2.Kryo的注册
和很多其他的序列化框架一样,Kryo为了提供性能和减小序列化结果体积,提供注册的序列化对象类的方式。在注册时,会为该序列化类生成int ID,后续在序列化时使用int ID唯一标识该类型。
注册的方式如下:
kryo.register(SomeClass.class);
或者
kryo.register(SomeClass.class, 1);
可以明确指定注册类的int ID,但是该ID必须大于等于0。如果不提供,内部将会使用int++的方式维护一个有序的int ID生成。
3.Kryo的Serializers
Kryo是序列化的框架,但是其具体的序列化逻辑并不是Kryo完成的,它提供了大量Serializer序列化器用于对相应的数据类型做序列化,以一种插件的方式集成进Kryo。比如Kryo支持大量数据类型的序列化
- 布尔、byte、char、short、int、long、float、double
- String、Collection
- Calendar、Date、TimeZone
- Enum、EnumSet
等等...,Kryo对这些对象都提供相应的Serializer,一支持其特定的序列化方式。
在Kryo中Serializer主要提供两个抽象方法供实现
abstract public void write (Kryo kryo, Output output, T object);
abstract public T read (Kryo kryo, Input input, Class<T> type);
用户可以根据需要自行扩展实现自己的序列化器Serializer。序列化器需要注册到Kryo,让Kryo用于处理特定类型的对象的序列化:
kryo.register(SomeClass.class, new SomeSerializer());
以上的大量的基本类型的序列化器由Kryo默认提供,并在初始化Kryo时已经注册。
但是大多数场景还是一些业务对象的序列化,并不是以上的默认类型。不可能每次序列化对象时,都需要编写相应的Serializer。对于这种场景,Kryo提供了默认的通用Serializer - FieldSerializer。大多数类的序列化都是使用该序列化器,它对public、protected、package的field使用bytecode genreation,对于private使用setAccessible和反射。
当然Kryo还提供了Java Serialization的Serializer实现JavaSerializer,但是这样该类就需要遵循Java规范,实现Serializable接口。
4.引用
Kryo允许多引用和循环引用,虽然开销很小,对此Kryo提供配置接口禁用以节省空间:
Kryo kryo = new Kryo();
kryo.setReferences(false);
5.Kryo的读和写的方式
Kryo提供三种读写对象的方式
如果序列化的对象类型未知并且可能为空:
kryo.writeClassAndObject(output, object);
// ...
Object object = kryo.readClassAndObject(input);
if (object instanceof SomeClass) {
// ...
}
如果对象类型已知并且可能为空:
kryo.writeObjectOrNull(output, someObject);
// ...
SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
如果对象类型已知并且不可能为空:
kryo.writeObject(output, someObject);
// ...
SomeClass someObject = kryo.readObject(input, SomeClass.class);
#### 三.Kryo的弊端
前文中介绍大多数类的序列化基本上使用了FieldSerializer,该Serializer不支持对序列化对象类的field的Add、Rename、Remove操作,即如果更改了对象的字段,然后再从更改前序列化的bytes中反序列化,将会出错。
当然如果想得到Add、Remove等操作的支持,可以使用FieldSerializer的其他扩展,如TaggedFieldSerializer、VersionFieldSerializer等等