MapReduce的默认编写及调用方式


我们都知道,Hadoop是最早产生的用来解决大数据处理的开源框架。在不停的发展迭代过程中,现在的Hadoop已经发展成为具有四大核心组件的一个基础平台:


1、HDFS:Hadoop的分布式文件系统


2、MapReduce:分布式计算程序的编成框架


3、YARN:资源调度系统/分布式的操作系统


4、Common:Hadoop中的,以上三大组件的底层支撑,主要提供了各种基础工具包和RPC通信框架



对于其中的MapReduce,这是最早产生的可以用来解决海量数据的计算问题,MapReduce是一种编程模型,该模型的计算是分布式的,分成两个阶段,第一个阶段是Mapper阶段,主要用来做任务的切分和并发执行,第二个阶段是Reducer阶段,主要用来对第一个阶段的所有任务的结果做最终汇总。



前面,我已经介绍了关于MapReduce的入门程序WordCount的编写


MapReduce--1--入门程序WordCount


这是最普通的一种运行方式。


在此我也整理了一个关于MapReduce编程的系列教程


MapReduce编程案例系列篇


感兴趣的可以持续关注,会一直不断的更新。



但是最近,研究了一下关于HDFS集群的shell操作命令的原理。其实通过观察hadoop fs 这个命令的底层实现,可以得知,该命令的执行,其实就执行org.apache.hadoop.fs.FsShell这个类。所以咱们来看一下这个类的实现:



只贴核心代码:


public class FsShell extends Configured implements Tool {

	public static void main(String argv[]) throws Exception {
		FsShell shell = newShellInstance();
		Configuration conf = new Configuration();
		conf.setQuietMode(false);
		shell.setConf(conf);
		int res;
		try {
			res = ToolRunner.run(shell, argv);
		} finally {
			shell.close();
		}
		System.exit(res);
	}

	/**
	 * run
	 */
	@Override
	public int run(String argv[]) throws Exception {
		

		/**
		* 通过命令工厂,获取命令实例
		*/
		Command instance = commandFactory.getInstance(cmd);

		/**
		* 运行 命令实例的  run 方法 执行业务处理
		* hadoop fs -ls /
		*/
		exitCode = instance.run(Arrays.copyOfRange(argv, 1, argv.length));
		return exitCode;
	}

	// 忽略掉其他非重点代码
}

该类的入口是main方法,main方法中的核心代码是:


res = ToolRunner.run(shell, argv);

那咱们再来看run方法的实现:


public static int run(Tool tool, String[] args) throws Exception {
	return run(tool.getConf(), tool, args);
}



继续看其中调用的main方式的实现:


public static int run(Configuration conf, Tool tool, String[] args) throws Exception {
	if (conf == null) {
		conf = new Configuration();
	}
	GenericOptionsParser parser = new GenericOptionsParser(conf, args);
	//set the configuration back, so that Tool can configure itself
	tool.setConf(conf);

	//get the args w/o generic hadoop args
	String[] toolArgs = parser.getRemainingArgs();
	return tool.run(toolArgs);
}

通过以上的调用关系可以看出ToolRunner.run(shell, argv),其实它的内部就是调用 shell.run(argv)


说白了,就是通过一个工具类来调用当前FsShell的执行,首先从main方法进入,然后兜转一圈,又回到调用FsShell实例对象的run方法来了。



所以,我们可以据此改写我们的第一个MapReduce程序WordCount:



编写一个类WordCountMRToolRunner extends Configured implements Tool即可。 然后我们把之前写入到main方法中的MapReduce的驱动程序代码写到run方法中即可。


然后依然由main方法作为程序入口。



下面是具体的代码实现:


package com.ghgj.mazh.mapreduce.wc.demo4;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
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;

/**
 * 作者: 马中华
 * 日期: 2017年10月28日下午2:48:06
 * 
 * 描述:
		此种WordCount的编写方式来自于hadoop fs 这个shell命令
 */
public class WordCountMRToolRunner extends Configured implements Tool {

	/**
	 * 日期:2017年10月28日下午3:13:42
	 * 
	 * 程序的入口。
	 */
	public static void main(String[] args) throws Exception {

		int run = ToolRunner.run(new WordCountMRToolRunner(), args);
		System.exit(run);
	}

	/**
	 * 观察main方法中的核心代码: ToolRunner中的run方法的实现即可得知:最后会调用第一个参数对象的run方法。
	 * 但事实上,run方法的第一个参数就是当前类的一个实例对象。所以,main方法最后会调用这个run方法执行。
	 */
	@Override
	public int run(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);

		job.setJarByClass(WordCountMRToolRunner.class);

		job.setMapperClass(WordCountMapper.class);
		job.setReducerClass(WordCountReducer.class);

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		FileInputFormat.setInputPaths(job, new Path("d:\\bigdata\\wordcount\\input"));
		Path outputPath = new Path("d:\\bigdata\\wordcount\\output");
		FileSystem fs = FileSystem.get(conf);
		if (fs.exists(outputPath)) {
			fs.delete(outputPath, true);
		}
		FileOutputFormat.setOutputPath(job, outputPath);

		boolean waitForCompletion = job.waitForCompletion(true);
		return waitForCompletion ? 0 : 1;
	}

	/**
	 * 作者: 马中华 
	 * 日期: 2017年10月28日下午2:47:19
	 * 
	 * 描述:
			Mapper组件
	 */
	public static class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {

		@Override
		protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

			String[] splits = value.toString().split(" ");
			for (int i = 0; i < splits.length; i++) {
				context.write(new Text(splits[i]), new IntWritable(1));
			}
		}
	}

	/**
	 * 作者: 马中华 
	 * 日期: 2017年10月28日下午2:47:38
	 * 
	 * 描述:
			Reducer组件
	 */
	public static class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

		@Override
		protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {

			int result = 0;
			for (IntWritable iw : values) {
				result += iw.get();
			}
			context.write(key, new IntWritable(result));
		}
	}
}



代码的运行结果就不贴了。最终,代码也是能顺利执行成功的。





希望对大家有用。  ヾ(◍°∇°◍)ノ゙