序列化 — 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM