在读Hadoop源码时发现DefaultStringifier这个类很有意思,主要是用来从configuration对象中get或set key、value键值对的时候使用。
具体原因不是很清楚,在网上google了下:
写MapReduce程序通常要传递各种各样的参数,选择合适的方式来传递参数既能提高工作效率,也可以避免bug的产生。根据参数的大小,可以粗略的分为以下几种。
最直接的方式就是使用Configuration的各种set方法,对于基本数据类型都有很好的支持,比如传递kmeans聚类算法的中心点个数。
如何传递一个对象型参数?话说所有的对象都是由基本类型构建的,所以我们可以覆盖这个对象的toString()方法,将它的所有元素表示成字符串,然后使用Configuration.set(name, value)传递这个字符串。然后在Mapper端获得这个字符串,做析构。这种朴素的方法有两个缺点。首先,将对象变成字符串会有精度上的损失,比如 double类型转换成字符串,不仅精度有损失,而且8字节的空间用字符串来表示可能会变成几十字节。其次,由于字符串化和反字符串化分散在不同的地方,很容易产生bug,如果修改了这个对象的结构,这种bug产生的几率非常大。既然有这种需求存在,难道hadoop没有提供nice点的方法吗?有,不过在api文档中没有直接说明。
正确的方法是,让这个对象实现Writable接口,使它具有序列化的能力,然后使用org.apache.hadoop.io.DefaultStringifier的store(conf, obj, keyname)和load(conf, keyname, itemclass)静态方法设置和获取这个对象。他的主要思想就是将这个对象序列化成一个字节数组后,用Base64编码成一个字符串,然后传递给 conf, 解析的时候与之类似。
如何传递更大的参数,比如分词用的语料库等等?可以使用hadoop的缓存文件DistributedCache。
1、使用configuration的set()和get()方法,这里的name和value都是String型
Configuration.set(name, value)
Configuration.get(name)
这种方法适合基本数据类型的传递。
2、使用Stringifier 接口。
DefaultStringifier.store(conf, object ,"key");将object以序列化后以指定的key存在conf中。
object = DefaultStringifier.load(conf, "key", variableClass );从conf中取出object。
需要指出的是使用第二种方法的对象必须是可序列化的。Hadoop的序列化是通过Writable接口来实现的,在org.apache.hadoop.io包下包含了大量的可序列化的组件,它们都实现了Writable接口,Writable接口提供了两个方法,write和readFields,分别用来序列化和反序列化。