[英]Kryo: deserialize old version of class
我需要通過添加兩個新參數來修改類。 這個類是用Kryo序列化的。 每當我停止我的流時,我目前正在持續保存與此課程相關的信息,作為RDD。 當我重新啟動流時,我加載了以前持久化的信息,並使用它們在我停止和重新啟動之間保持一致。
由於I類持久化需要這些新參數,我通過添加新的kryo.writeObject(output, object, ObjectSerializer)
和kryo.readObject(input, classOf[Object], ObjectSerializer)
來更改類和序列化器。參數。
現在,每當我重新啟動我的流時,我都會獲得一個異常:“遇到未注冊的類......”。
這似乎是顯而易見的,因為我試圖反序列化一個對象,這個對象在我停止流時我堅持的信息中沒有包含。 如果我刪除這些數據並啟動流,就好像它沒有任何先前的運行一樣,則不會發生異常。
有沒有辦法避免這種異常? 也許通過指定一些默認值,以防這些參數丟失?
謝謝
編輯:
我找到了一些我以前沒見過的有用的東西: Kryo問題194 。
這個人通過簡單地插入一個很長的定義他應該使用哪個版本的反序列化器來實現版本控制。 這是一個簡單的解決方案,但是,由於編寫我正在編寫的代碼的公司沒有考慮向前兼容性,我想我必須拋棄所有在新的序列化器之前保留的數據窗口。
如果有人能提出更好的解決方案,請告訴我。
編輯2:
仍然有這種情況的問題。 我嘗試使用此處描述的CompatibleFieldSerializer: CompatibleFieldSerializer示例因此,通過注冊此序列化程序而不是先前使用的自定義序列化程序。 結果是,現在,當重新加載持久數據時,它會產生java.lang.NullPointerException
。 如果之前沒有數據持續存在,仍然沒有問題。 我可以啟動我的流,序列化新數據,停止流,反序列化並重新啟動我的流。 仍然沒有解決方案的線索。
這個問題的解決方案是幾個月前發現的。 所以我想盡快發布這個問題的答案。 問題在於,由於代碼中的錯誤,該類被使用標准的Kryo FieldSerializer序列化,該標准不是向前兼容的。 我們必須執行以下操作來反序列化舊類並將其轉換為新的序列化類。
情況是:
case class ClassA(field1 : Long, field2 : String)
它像這樣序列化:
object ClassASerializer extends Serializer[ClassA] with Serializable{
override def write(kryo: Kryo, output: Output, t: ClassA) = {
output.writeLong { t.field1 }
output.writeString { t.field2 }
}
override def read(kryo: Kryo, input: Input, aClass: Class[ClassA]) =
classA(
field1 = input.readLong(),
field2 = input.readLong()
)
並且循環包含要使用序列化程序序列化的類的Seq,以便為所有類注冊所有序列化程序。
protected def registry: Seq[aClass: Class[A], serializer: Serializer[A]] = ...
final def register(kryo: Kryo) = {
registry.foreach { registrable => kryo.register(registrable.aClass, registrable.serializer) }
}
需要通過添加新字段來修改該類,該字段是另一個案例類的實例。
為了執行此類更改,我們必須使用與Kryo庫“可選”相關的注釋,
...
import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional
import scala.annotation.meta.field
...
case class ClassA(field1 : Long, field2 : String, @(Optional @field)("field3") field3 : ClassB)
序列化程序被修改,例如在讀取舊的序列化類時,它可以使用默認值實例化field3,並且在寫入時,寫入這樣的默認值:
object ClassASerializer extends Serializer[ClassA] with Serializable{
override def write(kryo: Kryo, output: Output, t: ClassA) = {
output.writeLong { t.field1 }
output.writeString { t.field2 }
kryo.writeObject(output, Option { t.field3 } getOrElse ClassB.default, ClassBSerializer)
}
override def read(kryo: Kryo, input: Input, aClass: Class[ClassA]) =
ClassA(
field1 = input.readLong(),
field2 = input.readLong(),
field3 = ClassB.default
)
還修改了kryo序列化程序注冊以注冊可選字段:
protected def registry: Seq[aClass: Class[A], serializer: Serializer[A]] = ...
def optionals = Seq("field3")
final def register(kryo: Kryo) = {
optionals.foreach { optional =>
kryo.getContext.asInstanceOf[ObjectMap[Any, Any]].put(optional, true) }
registry.foreach { registrable => kryo.register(registrable.aClass, registrable.serializer) }
}
結果我們能夠編寫序列化類的新版本。 之后,我們必須刪除可選的注釋,修改序列化程序以便從新的序列化類中讀取實際字段,並刪除可選的序列化程序注冊並將其添加到注冊表Seq。
與此同時,我們糾正了通過FieldSerializer強制序列化的代碼中的錯誤,但這不在問題的范圍內。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.