序列化 — Kryo序列化

一.Kryo介绍

Kryo是一个快速且高效的针对Java对象序列化的框架。它的特点:

  1. 序列化的性能非常高
  2. 序列化结果体积较小
  3. 提供了简单易用的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等等

参考

kryo

posted @ 2020-03-17 16:42  怀瑾握瑜XI  阅读(4848)  评论(0编辑  收藏  举报