使用kryo做序列化遇到的幾個坑

其中在整合kryo的時候,遇到了一些挑戰,記錄如下:

問題一:
在系列化有Hashtable、Hashmap之類的對象屬性的時候,會遇到異常:
com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): java.util.Collections$SynchronizedRandomAccessList
Serialization trace:
childs (xc.core.tree.Tree)
tree (xc.core.RegionTree)
    at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy.newInstantiatorOf(Kryo.java:1319)
    at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1127)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1136)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.create(CollectionSerializer.java:107)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:111)
    at com.esotericsoftware.kryo.serializers.CollectionSerializer.read(CollectionSerializer.java:40)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:731)

這個異常比較容易解決,簡單來說就是默認的系列化器不支持java.util.Collections$SynchronizedRandomAccessList這種沒有無參構造函數的類,解決辦法就是下載
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo-serializers</artifactId>
<version>0.42</version>
得到kryo-serializers-0.42.jar,這裏麪包含了很多特殊類的自定義系列化器,加入到工程中然後代碼中執行:
SynchronizedCollectionsSerializer.registerSerializers(kryo);
問題即可以得到解決。

問題二:
在反系列化時,會得到異常:
com.esotericsoftware.kryo.KryoException: Encountered unregistered class ID: XXXX
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:137)
    at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:693)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:804)
其中XXXX的值不定,這類異常,有幾種可能性存在,首先要了解一些kryo的基本原理:
當Kryo寫出一個對象的實例時,首先可能需要寫出一些標識對象類的東西。默認情況下,寫入完整類名,然後寫入該對象的字節。後續出現的同一類對象圖的對象用變長的int來寫(using a variable length int)。寫類的名字有點低效,所以類可以事先註冊:kryo.register(SomeClass.class);這樣的話,SomeClass 註冊到了 Kryo,它將該類與一個 int 型的 ID 相關聯。當 Kryo 寫出 SomeClass 的一個實例時,它會寫出這個 int ID。這比寫出類名更有效。在反序列化期間,註冊的類必須具有序列化期間相同的 ID 。
還有一種情況是kryo每次寫入類的完整信息,而不是通過int類型的ID號去代替。
兩種情況如何取捨呢?寫id的情況,效率會高一些,但是缺陷很明顯:所有系列化涉及到的類都需要代碼裏手工kryo.register(),否則報告unregistered class ID;另外,kryo不能保證"每次jvm重啓之後,或者在多臺jvm機器之間,同一個類註冊到kryo的class ID會相同",所以這就導致該模式應用於集羣會存在嚴重問題,所以用id代替類信息的模式不建議使用,而且,默認該功能也是關閉的,除非你在代碼中顯性的調用了以下代碼:
kryo.register(SomeClass.class);
或者:
Kryo.setRegistrationRequired(true);
所以遇到Encountered unregistered class ID之類的問題,首先要檢查該功能是否被不經意的開啓了。如果確保未開啓該功能,再考慮以下情況:就是對象太大,導致系列化結果不完整,因爲output對象默認的緩存字節數並不大,實際對象超出大小的時候,系列化的時候並不會報告錯誤,但是系列化結果已經不完整,從而導致反系列化的時候會失敗,報告的錯誤一般也是Encountered unregistered class ID。該問題的解決:
一般系列化的時候,代碼是這麼寫的:
ByteArrayOutputStream baos=new ByteArrayOutputStream();
Output output = new Output(baos);
這種情況下,緩存字節數大於4096的話,就會發生問題。必須在建立Output對象的時候,指定更大的bufferSize,例如:
ByteArrayOutputStream baos=new ByteArrayOutputStream();
Output output = new Output(baos,100000);
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章