一.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等等