现在我们已经有了mapper和reducer,下一步是写一个job驱动并在开发机器上的
测试数据上运行它。
在本地运行Job
本单第一节中介绍了Tool接口的使用,写一个驱动来运行我们的mapreduce job来找到
每一年的最大气温很容易(见例6-10)。
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
public class MaxTemperatureDriver extends Configured implements Tool {
@SuppressWarnings("deprecation")
@Override
public int run(String[] args) throws Exception {
if (args.length != 2) {
System.err.printf("Usage: %s [generic options] <input> <output>\n",
getClass().getSimpleName());
ToolRunner.printGenericCommandUsage(System.err);
return -1;
}
Job job = new Job(getConf(), "Max temperature");
job.setJarByClass(getClass());
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.setMapperClass(MaxTemperatureMapper.class);
job.setCombinerClass(MaxTemperatureReducer.class);
job.setReducerClass(MaxTemperatureReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new MaxTemperatureDriver(), args);
System.exit(exitCode);
}
}
MaxTemperatureDriver实现了Tool接口,所以我们可以设置GenericOptionsParser支持的选项。run()方法
基于tool的配置创建了一个Job对象,并用它来运行job。在job配置参数中,我们设置输出和输出文件路径;mapper,
reducer及combiner类;及输出类型(输入类型由input format决定,默认为TextInputFormat,键为LongWritable,
值为Text)。给job设置一个名字也是一个好想法(Map temperature),这样在运行期间或执行结束,你可以在一
系列job中找到它。默认,名称是JAR文件的名称,它通常不具有描述性。
现在我们使用本地文件来运行了这个应用。HADOOP自带了一个本地job runner,一个简化版的mapreduce运行
引擎,用来在单一JVM上运行mapreduce job。它用来进行测试,在IDE中使用非常方便,你可以在调度器中运行它,
来一步步跟踪你的mapper和reducer代码。
如果mapreduce.framework.name设置为local(默认值),那么会使用本地的job runner。
在命令行,我们可以输入以下命令来运行驱动:
% mvn compile
% export HADOOP_CLASSPATH=target/classes/
% hadoop v2.MaxTemperatureDriver -conf conf/hadoop-local.xml \
input/ncdc/micro output
或者,我们可以使用GenericOptionsParser提供的fs和jt选项:
% hadoop v2.MaxTemperatureDriver -fs file:/// -jt local input/ncdc/micro output
这个命令执行MaxTemperatureDriver,使用本地input/ncdc/micro目录做为输入,输出放到本地
out目录。注意,尽管我们设置-fs以使用本地文件系统(file:///),本地job runner可以在任意文件系统
上运行,包括HDFS(如果HDFS上只有少量的文件这样做是很方便的)。
我们可以在本地文件系统查看输出:
% cat output/part-r-00000
1949 111
1950 22
驱动测试
实现Tool,不仅可以得到它提供的灵活的配置选项,并且可以让它更具有测试性,因为它允许你注入
任意的配置。你可以利用它来写一个测试,使用本地的job runne在已知的输入数据上来运行一个job,然后
检查输出是否正确。
有两种方式来做这件事。第一种是用本地的job runner使用测试文件来运行job。例6-11给了这样做的一
个思路。
//Example 6-11. A test for MaxTemperatureDriver that uses a local, in-process job runner
@Test
public void test() throws Exception {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "file:///");
conf.set("mapreduce.framework.name", "local");
conf.setInt("mapreduce.task.io.sort.mb", 1);
Path input = new Path("input/ncdc/micro");
Path output = new Path("output");
FileSystem fs = FileSystem.getLocal(conf);
fs.delete(output, true); // delete old output
MaxTemperatureDriver driver = new MaxTemperatureDriver();
driver.setConf(conf);
int exitCode = driver.run(new String[] { input.toString(),
output.toString() });
assert exitCode==0;
//checkOutput(conf, output);
}
这个测试设置了fs.defaultFS和mapreduce.framework.name,所以它使用本地文件系统和本地的job runner。
然后它通过Tool接口在一小部分已知数据上运行MaxTemperatureDriver。在测试的最后,调用checkOutput()方法
来一行行比较实际输出与预期输出。
第二种测试驱动的方法 是使用“mini-”集群。HADOOP有一系统测试类,叫MiniDFSCluster,MiniMRCluster,
及MiniYARNCluster,它提供了创建进程内集群的程序化方法。不同于本地job runner,它们允许在全部HDFS,
MapReduce,YARN机器上测试。同时也要记住,mini-cluster中的node manager使用独立的JVM运行任务,使
调度变得更加困难。
你可以在命令行运行一个mini-cluster,输入以下命令
<pre name="code" class="plain">% hadoop jar \
$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-*-tests.jar \
minicluster
HADOOP自己的自动化测试中广泛使用mini-cluster,但它们也可以用来测试用户代码。HADOOP的
MapReduceTestCase抽象类提供写一样一个测试的基础,在它的setUp()方法与tearDown()方法中处理了开启与
结束的细节,并产生一个合适的Configuration对象。子类只需要把数据移到HDFS(可能通过拷贝本地文件),
运行mapreduce job,并确认输出是否正确。
这有点像是回归测试,并且可以存储输入的边界及预期值。随着你遇到更多的测试情况,你可以相应的
把他们加到输入文件,并更新预期输出文件。