一旦程序按预期通过小型数据集的测试,就可以考虑把他放到集群上运行了。当运行程序对整个数据进行测试的时候,可能会暴露更多的问题,这些问题可以像前面一样修复,即通过扩展测试用例及修改mapper或reducer函数的方式来应对新情况。在集群中调试程序很具有挑战性,我们来看一些常用的技术使其变得更简单一些。
程序可以正确运行之后,如果想进行一些优化调整,首先需要执行一些标准检查,借此加快MapReduce程序的运行速度,然后再做任务剖析(task profiling)。分布式程序的分布并不简单,Hadoop提供了钩子(hook)来辅助这个分析过程。
Hadoop中的组件是通过Hadoop自己的配置API来配置的。一个Configuration类的实例(可以在org.apache.hadoop.conf包中找到)代表配置属性及其取值的一个集合。每个属性由一个String来命名,而值的类型可以是多种类型之一,包括Java基本类型(如boolean、int、long和float)、其他有用的类型(如String、Class和java.io.File)及String集合。
Configuration从资源(即使用简单结构定义名值对的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位于上面的文件中,我们可以通过如下代码访问其属性:
Configuration conf = new Configuration(); conf.addResource("configuratoin-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属性一样。
1.资源合并
使用多个资源文件来定义一个Configuration时,事情变得有趣了。在Hadoop中,这用于分离(core-default.xml文件内部定义的)系统默认属性与(core-site.xml文件中定义的)位置相关(site-specific)的覆盖属性。下面范例中的文件定义了size属性和weight属性。
<?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");
后来添加到资源的属性会覆盖(override)之前定义的属性。所以,size属性的取值来自于第二个配置文件configuration-2.xml:
assertThat(conf.getInt("size", 0), is(12));
不过,被标记为final的属性不能被后面的定义被覆盖。在第一个配置文件中,weight属性的final状态是true,因此,第二个配置文件中的覆盖设置失败,weight取值仍然是第一个配置文件中的heavy:
assertThat(conf.get("weight"), is("heavy"));
试图覆盖final属性通常意味着配置错误,所以最后会弹出警告消息来帮助进行故障诊断。一般来说,管理员将守护进程站点中的属性标记为final,表明他们不希望用户在客户端的配置文件或作业提交参数(job submission parameter)中有任何改动。
2.变量扩展
配置属性可以用其他属性或系统属性进行定义。例如,在第一个配置文件中的size-weight属性可以定义为${size}和${weight},而且这些属性是用配置文件中的值来扩展的:
assertThat(conf.get("size-weight"), is("12,heavy"));
系统属性的优先级高于资源文件中定义的属性:
System.setProperty("size", "14"); assertThat(conf.get("size-weight"), is("14 , heavy"));该特性特别适用于在命令行方式下用JVM参数-Dproperty=value来覆盖属性。
注意,虽然配置属性可以通过系统属性来定义,但除非系统属性使用配置属性重新定义,否则,他们是无法通过配置API进行访问的。因此:
System.setProperty("size", "2"); assertThat(conf.get("length"), is((String) null));