在第二章中,我们介绍了MapReduce模式。在本章中,我们看看在实际中开发一个MapReduce
应用。
写一个MapReduce程序要遵循一个特定的模式。开始时你要写map和reduce函数,最好把单元
测试也写上,确保程序做了你想做的。然后你写一个驱动程序来运行一个job,它可以使用数据的一
小部分在你的集成开发环境中运行以检查它是否正常工作。如果失败,你需要使用你的IDE调试器来
找到程序的问题。然后你可以扩展你的单元测试来覆盖这种情况,促使你的map或reduce可以正确
处理这种输入。
当程序在部分数据集上运行与预期一致时,你就可以把它发布到集群上了。运行在完整数据集上
可能暴露更多的问题,你可以象之前那样修正它,通过扩展你的测试并修改你的map和reduce来处理
新的问题。在集群中调试是一个挑战,所以我们来查看一些可以使它简单的常用的技术。
在程序工作后,你也许希望做一些调整,首先,运行一些标准测试来使mapreduce程序更快,
其次是任务分析。分析分布式程序不是那么容易的,但HADOOP有一些针对这个处理的工具。
在我们开始写一个mapreduce程序之前,我们需要建立并配置开发环境。为了它,我们需要学习
一些关于HADOOP如何配置的东西。
配置API
HADOOP组件使用HADOOP自己的配置API来配置。类Configuration的实例(org.apache.hadoop.conf
包中)表示了配置的属性和值的集合。每一个属性的名称是一个String,值可能是java基本数据类型中的一
个,包括boolean,int ,long,float等,其它一些有用的类型如:String,Class及java.io.File;及String集合。
Configuration从XML文件中读取它们的属性,XML文件定义了简单的name-value对的结构。见例6-1.
Example 6-1. A simple configuration file, configuration-1.xml <?xml version="1.0"?> <configuration> <property> <name>color</name> <value>yellow</value> <description>Color</description> </property> <property> <name>size</name> <value>10</value> <description>Size</description> </property> <property> <name>weight</name> <value>heavy</value> <final>true</final> <description>Weight</description> </property> <property> <name>size-weight</name> <value>${size},${weight}</value> <description>Size and weight</description> </property> </configuration>
假设这个配置在一个名为configuration-1.xml的文件中,我们可以使用如下的代码来访问它的属性:
Configuration conf = new Configuration(); conf.addResource("configuration-1.xml"); assertThat(conf.get("color"), is("yellow")); assertThat(conf.getInt("size", 0), is(10)); assertThat(conf.get("breadth", "wide"), is("wide"));
有两件事需要注意:类型信息没有保存在XML文件中;属性在读取时可以解释为指定的类型。同时,
get()方法允许你指定一个默认的值,用在属性没有定义在XML文件的情况,如这里的breadth。
资源整合
定义一个Configuration时使用多个资源文件,情况就变得有趣了。这个在HADOOP中用来分离出系统
的默认属性,定义在内部的一个叫core-default.xml的文件中,覆盖core-site.xml中的特定于平台的属性。
例6-2定义了size和weight属性。
Example 6-2. A second configuration file, configuration-2.xml <?xml version="1.0"?> <configuration> <property> <name>size</name> <value>12</value> </property> <property> <name>weight</name> <value>light</value> </property> </configuration>
资源按序添加到Configuration:
Configuration conf = new Configuration(); conf.addResource("configuration-1.xml"); conf.addResource("configuration-2.xml");
定义在后加的资源文件中的属性会覆盖前面定义的。所以size属性取的值是第二个配置文件:
assertThat(conf.getInt("size", 0), is(12));
尽管如此,标记了final的属性不能被覆盖。在第一个配置文件中,weight属性是final的,所以第二个文件试图
覆盖时会失败,它从第一个文件中取值:
assertThat(conf.get("weight"), is("heavy"));
试图覆盖final属性通常导致配置错误,其结果是在日志文件中有一条警告信息来辅助诊断。管理员把属性标记
为final表示他不希望用户在他们的客户端文件或提交参数中修改它。
变量扩展
配置属性可以通过其它属性或系统属性的形式定义。例如,第一个配置文件中的属性size-weight定义为${size},${weight},
这些属性使用配置中的值来扩展:
assertThat(conf.get("size-weight"), is("12,heavy"));
System的属性比定义在资源文件中的属性优先:
System.setProperty("size", "14"); assertThat(conf.get("size-weight"), is("14,heavy"));
这个特性很有用,可以通过命令行来覆盖属性,通过JVM的-Dproperty=value参数来定义。
注意,尽管配置属性可以以系统属性的形式定义,但是它不能通过配置API访问,除非系统属性以配置属性的形式重新定义:
System.setProperty("length", "2"); assertThat(conf.get("length"), is((String) null));