序列化

这篇文章是关于序列化方法在storm 0.6.0版及之前版本中是如何工作的。0.6.0版之前,storm使用一种不同的序列化方法,参见 Serialization (prior to 0.6.0)

元组可由任何一种类型的对象组成。由于storm是一个分布式系统,当对象在任务之间传递时,它需要知道如何序列化和反序列化这些对象。
Storm使用Kryo进行序列化。Kryo是一个灵活快速的序列化库,产生小的序列化。
默认情况下,storm能序列化原始类型:String、字节数组、ArrayList、HashMap、HashSet及Clojure的集合类型。如果你想在元组中使用另外的类型,你需要注册一个自定义序列化装置。
 
动态类型
元组中字段没有声明类型。你放置对象到字段中,storm动态地计算出序列化。我们获到序列化接口之前,让我们花点时间理解为什么storm的元组是动态类型。
如果添加静态类型到元组字段将使Storm API非常复杂。例如,Hadoop,它的key和value是静态类型,但需要非常多的注解。使用Hadoop API是一个负担,这样子做到类型安全是不值得的。动态类型简单易用。
此外,不可能用合理的方式静态化storm元组的类型。假如一个bolt订阅多个数据流,这些数据流中的字段可能使用不同的数据类型。当一个bolt在execute方法中接收一个元组后,这个元组可以来自于任意一个数据流,因此元组的数据类型可以是任意数据类型的组合。这里也许你可以使用一些反射技巧,为一个bolt订阅的不同数据流中的元组声明不同的方法,但storm使用简单直接的方式实现动态类型。
最后,使用动态类型的另一个原因是因为允许动态类型语言以简单的方式使用storm,像Clojure和Ruby。
 
自定义序列化
如上所述,Storm使用Kryo进行序列化。为了实现自定义序列化,你需要注册新的序列化装置和Kryo,强烈推荐你看看Kryo的主页,了解它如何处理自定义序列化。
通过拓扑配置的“topology.kryo.register”属性添加自定义序列化。它需要一个注册清单,其中的每个注册都可以采用以下两种形式之一:
1. 你要注册的类名。在这种情况下,storm使用Kryo的“FieldsSerializer”来序列化这个类。对这个类来说,这不一定是最优的,更多细节参见Kryo文档。
2. 你要注册的类名和一个com.esotericsoftware.kryo.Serializer接口的实现。
让我们看一个例子:
  1. topology.kryo.register: 
  2.   - com.mycompany.CustomType1 
  3.   - com.mycompany.CustomType2: com.mycompany.serializer.CustomType2Serializer 
  4.   - com.mycompany.CustomType3 
com.mycompany.CustomType1和com.mycompany.CustomType3的序列化使用FieldsSerializer。但com.mycompany.CustomType2的序列化使用com.mycompany.serializer.CustomType2Serializer。
Storm使用拓扑配置注册序列化装置提供了帮助。Config类的registerSerialization方法把注册的序列化装置添加到配置。
这里有一个称之为Config.TOPOLOGY_SKIP_MISSING_KRYO_REGISTRATIONS的高级配置。如果你设置它为真,storm将忽略任何已注册的序列化装置,就算classpath中没有它们的代码可用;否则,当storm未找到一个序列化装置时,将抛出异常。如果你在一个集群中运行多个拓扑,每个拓扑使用不同的序列化方式,但你想在storm.yaml文件中对这些拓扑声明各自的序列化方式,这个配置就非常有用。
 
Java序列化
如果storm遇到一个未注册序列化装置的类型,它将使用java序列化。如果对象不能用java序列化,storm将抛出异常。
注意,Java序列化是非常昂贵的,不管是CPU的花费,还是被序列化后对象所占的空间。在生产环境运行拓扑,强烈建议你注册自定义序列化装置。
通过设置Config.TOPOLOGY_FALL_BACK_ON_JAVA_SERIALIZATION为假,你可以关闭这个行为,回退到使用java序列化。