1. 程式人生 > >Apache Storm 官方文件 —— 序列化

Apache Storm 官方文件 —— 序列化

原文連結    譯者:魏勇

本文闡述了 Storm 0.6.0 以上版本的序列化機制。在低於 0.6.0 版本的 Storm 中使用了另一種序列化系統,詳細資訊可以參考 Serialization (prior to 0.6.0) 一文。

Storm 中的 tuple 可以包含任何型別的物件。由於 Storm 是一個分散式系統,所以在不同的任務之間傳遞訊息時 Storm 必須知道怎樣序列化、反序列化訊息物件。

Storm 使用 Kryo 對物件進行序列化。Kryo 是一個生成小序列的靈活、快速的序列化庫。

Storm 本身支援基礎型別、字串、位元組陣列、ArrayList、HashMap、HashSet 以及 Clojure 的集合型別的序列化。如果你需要在 tuple 中使用其他的物件型別,你就需要註冊一個自定義的序列化器。

動態型別

在 tuple 中沒有對各個域(field)的直接型別宣告。你需要將物件放入對應的域中,然後 Storm 可以動態地實現物件的序列化。在學習序列化介面之前,我們先來了解一下為什麼 Storm 的 tuple 是動態型別化的。

為 tuple 的 fields 增加靜態型別會大幅增加 Storm 的 API 的複雜度。比如 Hadoop 就將它的 key 和 value 都靜態化了,這就要求使用者自己新增大量的註解。使用 Hadoop 的 API 非常繁瑣,而相應的“型別安全”並不會為應用帶來多大的好處。相對的,動態型別就非常易於使用。

更進一步而言,也不可能有什麼合理的方法將 Storm 的 tuple 的型別靜態化。假如一個 Bolt 訂閱了多個 stream,從這些 stream 傳入的 tuple 很可能都帶有不同的型別。在 Bolt 的 execute 方法接收到一個 tuple 的時候,這個 tuple 可能來自任何一個 stream,也可能包含各種組合型別。也許你可以使用某種反射機制來為 bolt 訂閱的每個 stream 宣告一個方法類處理 tuple,但是 Storm 可以提供一種更簡單、更直接的動態型別機制來解決這個問題。

最後要說明的是,Storm 使用動態型別定義的另一個原因就是為了支援 Clojure、JRuby 這樣的動態型別語言。

定製序列化

前面已經提到,Storm 使用 Kryo 來處理序列化。如果要實現自定義的序列化生成器,你需要註冊一個新的機遇 Kryo 的序列化生成器。強烈建議讀者先仔細閱讀 Kryo 主頁 來理解它是怎樣處理自定義的序列化的。

可以通過拓撲的 topology.kryo.register 屬性來新增自定義序列化生成器。該屬性接收一個註冊列表,每個註冊項都可以使用以下兩種註冊格式中的一種格式:

  1. 只有一個待註冊的類的名稱。在這種情況下,Storm 會使用 Kryo 的 FieldsSerializer
    來序列化該類。這也許並不一定是該類的最優化方式 —— 可以檢視 Kryo 的文件來了解更多細節內容。

我們來看一個例子:

topology.kryo.register:
  - com.mycompany.CustomType1
  - com.mycompany.CustomType2: com.mycompany.serializer.CustomType2Serializer
  - com.mycompany.CustomType3

這裡 com.mycompany.CustomType1com.mycompany.CustomType3 會使用 FieldsSerializer,而com.mycompany.CustomType2 則會使用 com.mycompany.serializer.CustomType2Serializer 來實現序列化。

在拓撲的配置中,Storm 提供了用於註冊序列化生成器的選項。Config 類有一個 registerSerialization 方法可以將序列化生成器註冊到配置中。

Config 中有一個更高階的配置項叫做 Config.TOPOLOGY_SKIP_MISSING_KRYO_REGISTRATIONS。如果你將該項設定為 true,Storm 會忽略掉所有已註冊但是在拓撲的 classpath 中沒有相應的程式碼的序列化器。如果不這麼做,Storm 會在無法查詢到序列化器的時候丟擲錯誤。如果你在叢集中執行有多個拓撲並且每個拓撲都有不同的序列化器,但是你又想要在storm.yaml 中宣告好所有的序列化器,在這種情況下這個配置項會有很大的幫助。

Java 序列化

如果 Storm 發現了一個沒有註冊序列化器的型別,它會使用 Java 自帶的序列化器。如果這個物件無法被 Java 序列化器序列化,Storm 就會丟擲異常。

注意,Java 自身的序列化機制非常耗費資源,而且不管在 CPU 的效能上還是在序列化物件的大小上都沒有優勢。強烈建議讀者在生產環境中執行拓撲的時候註冊一個自定義的序列化器。保留 Java 的序列化機制主要為了便於設計新拓撲的原型。

你可以通過將 Config.TOPOLOGY_FALL_BACK_ON_JAVA_SERIALIZATION 配置為 false 的方式來將序列化器回退到 Java 的序列化機制。

特定元件的序列化註冊

Storm 0.7.0 支援對特定元件的配置(詳情請參閱Storm 的配置一文)。當然,如果某個元件定義了一個序列化器,這個序列化器也需要能夠支援其他的 bolt —— 否則,後續的 bolt 將會無法接收來自該元件的訊息!

在拓撲提交的時候,拓撲會選擇一組序列化器用於在所有的元件間傳遞訊息。這是通過將特定元件的序列化器註冊資訊與普通的序列化器資訊融合在一起實現的。如果兩個元件為同一個類定義了兩個序列化器,Storm 會從中任意選擇一個。

如果在兩個元件的序列化器註冊資訊衝突的時候需要強制使用一個序列化器,可以在拓撲級的配置中定義你想要的序列化器。對於序列化器的註冊資訊,拓撲中配置的值是優先於具體元件的配置的。