1. 程式人生 > >使用kryo做序列化會遇到的幾個坑

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

這個異常比較容易解決,簡單來說就是預設的系列化器不支援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();