Hadoop 的版本0.20包含一个新的java MapReduce API,我们也称他为上下文对象(context object)。新的API在类型虽然不兼容先前的API,但是更容易扩展。
新增的API和旧的API之间的不同点:
1、 新的API倾向于使用抽象类,而不是接口,是为了更容易扩展。
例如:可以不需要修改类的实现而在抽象类中添加一个方法。在新的API中,mapper和reducer现在都是抽象类;
2、 新的API放在org.apache.hadoop.mapreduce包(和子包)中。老版本的API依然在org.apache.hadoop.mapred中。
3、 新的API充分使用上下文对象,使用户代码能与MapReduce系统通信。
例如,MapContext基本具备了JobConf、OutputCollector和Reporter的功能。
4、 新的API同时支持“推(push)”和“拉(pull)”式的迭代。这两类API,均可以将
键/值对记录推给mapper,但除此之外,新的API也允许把记录从map()方式中拉出。对reducer来说是一样的。“拉”式处理数据的好处是可以实现数据的批量处理,而非逐条记录的处理。
5、 新增的API实现了配置的统一。旧的API通过一个特殊的JobConf对象配置作业,该对象是Hadoop配置对象的一个扩展(用于配置守护进程)。在新的API中,我们丢弃这种区分,所有的配置都是通过Configuration来完成。
6、 新API中作业控制有Job类实现,而非JobClient类,新API中删除了JobClient类。
7、 输出文件的命名文件不同,map的输出文件名为part-m-nnnnn,而reduce的输出为part-r-nnnnn(其中nnnnn表示分块序号,为整数,且从0开始算)。
旧MapReduce代码:
import java.io.IOException; import java.util.Iterator; 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.mapred.FileInputFormat; import org.apache.hadoop.mapred.FileOutputFormat; import org.apache.hadoop.mapred.JobClient; import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.MapReduceBase; import org.apache.hadoop.mapred.Mapper; import org.apache.hadoop.mapred.OutputCollector; import org.apache.hadoop.mapred.Reducer; import org.apache.hadoop.mapred.Reporter; public class WordCountApp { public static final String INPUT_PATH = "hdfs://itcast225:9000/hello"; //输出路径必须是不存在的 public static final String OUTPUT_PATH = "hdfs://itcast225:9000/output"; /** * 驱动代码 */ public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
final JobConf job = new JobConf(WordCountApp.class); //如果需要打成jar运行,需要下面这句 job.setJarByClass(WordCountApp.class); job.setMapperClass(MyMapper.class); job.setReducerClass(MyReducer.class); //告诉job执行作业时的输入路径 FileInputFormat.setInputPaths(job, INPUT_PATH); //告诉job执行作业时的输出路径 FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH)); //指明输出的k3类型 job.setOutputKeyClass(Text.class); //指明输出的v3类型 job.setOutputValueClass(IntWritable.class); //让作业运行,直到运行结束,程序退出 JobClient.runJob(job); } /** * KEYIN 即k1 表示每一行的起始字节偏移量 * VALUEIN 即v1 表示每一行的文本内容 * KEYOUT 即k2 表示每一行被拆分的单词 * VALUEOUT 即v2 表示每一行被拆分的单词次数 */ static class MyMapper extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable>{ @Override public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException { System.out.println("map的输入:<"+key.get()+","+value.toString()+">"); final String[] splited = value.toString().split(" "); for (String word : splited) { //key2 表示该行中的单词 final Text key2 = new Text(word); //value2 表示单词在该行中的出现次数 final IntWritable value2 = new IntWritable(1); //把k2、v2写入到context中 output.collect(key2, value2); System.out.println("map的输出:<"+key2.toString()+","+value2.get()+">"); } } } /** * KEYIN 即k2 * VALUEIN 即v2 * KEYOUT 即k3 * VALUEOUT 即v3 */ static class MyReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable>{ @Override public void reduce( Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException { System.out.println("reduce的输入:<"+key.toString()+","+values.toString()+">"); int sum = 0; while (values.hasNext()) { IntWritable count = (IntWritable) values.next(); sum += count.get(); System.out.println("reduce中for循环:"+count); } //到这里了,sum表示该单词key出现的总次数 //key3与key2相同 final Text key3 = key; //value3表示单词出现的总次数 final IntWritable value3 = new IntWritable(sum); output.collect(key3, value3); System.out.println("reduce的输出:<"+key3.toString()+","+value3.get()+">"); }
} } |
新MapReduce代码:
package mapreduce; import java.io.IOException; import org.apache.hadoop.conf.Configuration; 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.Partitioner; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCountApp { public static final String INPUT_PATH = "hdfs://itcast221:9000/hmbbs"; //输出路径必须是不存在的 public static final String OUTPUT_PATH = "hdfs://itcast221:9000/output"; /** * 驱动代码 */ public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException { final Job job = new Job(new Configuration(), WordCountApp.class.getName()); //如果需要打成jar运行,需要下面这句 job.setJarByClass(WordCountApp.class); job.setMapperClass(MyMapper.class); job.setReducerClass(MyReducer.class); job.setPartitionerClass(MyPartitoner.class); job.setNumReduceTasks(2); //告诉job执行作业时的输入路径 FileInputFormat.setInputPaths(job, INPUT_PATH); //告诉job执行作业时的输出路径 FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH)); //指明输出的k3类型 job.setOutputKeyClass(Text.class); //指明输出的v3类型 job.setOutputValueClass(IntWritable.class); //让作业运行,直到运行结束,程序退出 job.waitForCompletion(true); } static class MyPartitoner extends Partitioner<Text, IntWritable>{ @Override public int getPartition(Text key, IntWritable value, int numPartitions) { return key.toString().startsWith("hello")?0:1; } } /** * KEYIN 即k1 表示每一行的起始字节偏移量 * VALUEIN 即v1 表示每一行的文本内容 * KEYOUT 即k2 表示每一行被拆分的单词 * VALUEOUT 即v2 表示每一行被拆分的单词次数 */ static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{ /** * key 表示每一行的起始字节偏移量 * value 表示每一行的文本内容 * context 表示上下文环境,我们的输出是需要写入到该对象中的 */ protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException { System.out.println("map的输入:<"+key.get()+","+value.toString()+">"); final String[] splited = value.toString().split(" "); for (String word : splited) { //key2 表示该行中的单词 final Text key2 = new Text(word); //value2 表示单词在该行中的出现次数 final IntWritable value2 = new IntWritable(1); //把k2、v2写入到context中 context.write(key2, value2); System.out.println("map的输出:<"+key2.toString()+","+value2.get()+">"); } }; } /** * KEYIN 即k2 * VALUEIN 即v2 * KEYOUT 即k3 * VALUEOUT 即v3 */ static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{ /** * key 即k2 * values 即v2的集合 * context 上下文对象 */ protected void reduce(Text key, java.lang.Iterable<IntWritable> values, Context context) throws java.io.IOException ,InterruptedException { System.out.println("reduce的输入:<"+key.toString()+","+values.toString()+">"); int sum = 0; for (IntWritable count : values) { sum += count.get(); System.out.println("reduce中for循环:"+count); } //到这里了,sum表示该单词key出现的总次数 //key3与key2相同 final Text key3 = key; //value3表示单词出现的总次数 final IntWritable value3 = new IntWritable(sum); context.write(key3, value3); System.out.println("reduce的输出:<"+key3.toString()+","+value3.get()+">"); }; } } |
代码上的区别,新map,reduce方法不用继承
MapReduceBase类,传入的参数由原来的4个变成现在的3个。去掉
OutputCollector<Text, IntWritable> output, Reporter reporter
由一个新的类 Context封装了上述信息。
新的Job类对象,由Configuration创建的,也封装了JobClient的方法。