Hadoop MapReduce初窥-wordcount示例
来源:互联网 发布:多台docker php-fpm 编辑:程序博客网 时间:2024/05/16 15:46
MapReduce是一种可用于并行处理大规模集群上的海量数据的编程模型。它的出现使得并行处理海量数据变的更容易,容错性更高。本文借助wordcount程序介绍MapReduce的一些基本知识。本文在Eclipse环境中开发,然后编译成jar包,放单节点的伪集群中运行。
MapReduce程序由Mapper类,Reducer类以及一些用于运行作业的代码完成。map负责把任务分解成多个任务,reduce负责把分解后多任务处理的结果汇总起来。MapReduce框架处理键值对形式的数据,处理框架作业的输入是键值对形式的集合,处理后的输出也是同样形式,但并不要求输入和输出的数据类型是一样的。一个MapReduce作业的输入输出流程中各环节的数据形式如下:
(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)
WordCount过程解析
Map阶段:首先对待处理的文件按照文件块(例如当前默认的256M)的大小进行切分,切分后的split作为Map任务的输入,每个split对应一个map任务进行并行处理(由此可见HDFS上过多的小文件,不仅会加重HDFS存储时NameNode的负担,也会造成MapReduce资源的调度的压力)。Map输入的key,默认为LongWritable,表示该行数据的起始位置相对于整个文件位置的偏移量。在本文中,并不关心该偏移量,故将其类型设置为Object,也不对其进行处理。例如,如果输入的文本内容如下:
hello hadoophello mapreduce
那么,map输入的key和value,如下:
(0,hello hadoop) //key为0,是第一行文本的偏移量(12,hello mapreduce) //key为12,因为第二行跳过了第一行的12个字符
经过map阶段对每行文本进行单词切分,并将每个单词的计数置为1,输出如下:
(hello,1)(hadoop,1)(hello,1)(mapreduce,1)
Combiner阶段(非必须):会对map的输出进行本地的合并,减少网络的传输。
(hadoop,[1])(hello,[2]) //如果没有combiner,(hello,1)会分两次传输(mapreduce,[1])
Reduce阶段:对于map的结果进行合并,输入的key是单词,value是相同单词计数的列表。在数据从map到reduce的过程,还需经历shuffle阶段,此过程根据map输出的key进行重新排序和分组。根据排序和分组的结果,hadoop框架会决定reduce任务的分配。
代码示例:
package com.test.wordcount;import java.io.IOException;import java.util.regex.Pattern;import org.apache.hadoop.conf.Configuration;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.Mapper;import org.apache.hadoop.mapreduce.Reducer;import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;public class WordCount { public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{ //创建正则模式,"\\W+"表示所有非英文字母的字符 private static final Pattern splits = Pattern.compile("\\W+"); private final static IntWritable one = new IntWritable(1); private Text word = new Text(); protected void setup(Context context) { //do something initial work } public void map(Object key, Text value, Context context) throws IOException, InterruptedException { //以所有非英文字符作为分隔符,切分输入的值 String [] words = splits.split(value.toString()); for(int i = 0; i < words.length; ++i ) { word.set(words[i]); //调用Text类的方法,设置结果 context.write(word, one); //context实例用于将map处理结果以键值对的形式输出 } } protected void cleanup(Context context) { //do something clean up work } } public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> { private IntWritable result = new IntWritable(); public void reduce(Text key, Iterable<IntWritable> values,Context context ) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) { sum += val.get(); } result.set(sum); context.write(key, result); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "wordcount"); job.setJarByClass(WordCount.class); job.setMapperClass(TokenizerMapper.class); job.setCombinerClass(IntSumReducer.class); job.setReducerClass(IntSumReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); }}
代码分析
Map
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{ //创建正则模式,"\\W+"表示所有非英文字母的字符 private static final Pattern splits = Pattern.compile("\\W+"); private final static IntWritable one = new IntWritable(1); private Text word = new Text(); protected void setup(Context context) { //do something initial work } public void map(Object key, Text value, Context context) throws IOException, InterruptedException { //以所有非英文字符作为分隔符,切分输入的值 String [] words = splits.split(value.toString()); for(int i = 0; i < words.length; ++i ) { word.set(words[i]); //调用Text类的方法,设置结果 context.write(word, one); //context实例用于将map处理结果以键值对的形式输出 } } protected void cleanup(Context context) { //do something clean up work }}
map函数由Mapper实现,mapper类中声明了map()虚方法,此外还有run(),setup()和cleanup()。run()方法会根据上下文环境,判断是否还有输入,如果有,则不断的调用map函数进行处理。setup()用于在TokenizerMapper第一次实例化时,做一些必要的初始化工作;cleanup()则是在数据处理结束后提供需要的清理工作。当然,如果没有特别要初始化或者清理的对象,两者也没必要一定要实现。
Mapper的泛型参数[Object, Text, Text, IntWritable];分别是输入和输入的键值对,即Map的输入类型是[Object, Text],输出是[Text, IntWritable]。而Text,IntWritable等是实现了Writable接口,提供了Hadoop特有的序列化方式的类型,可以理解为int,String等类型在Hadoop中的包装。
Text类类似于Java中的String,IntWritable相当于int型。此外还有ObjectWritbale,NullWritable,ByteWritable,BooleanWritable,ShortWritable,FloatWritable,LongWritable,DoubleWritable等。此外还可以自定义Witebale类型,例如如下方式的自定义类型,readFields()和write()是必须要实现的方法:
public class MinMaxCountTuple implements Writable { private int Min ; private int Max ; MinMaxCountTuple(int min, int max) { // } @Override public void readFields(DataInput in) throws IOException { // TODO Auto-generated method stub Min = in.readInt(); Max = in.readInt(); } @Override public void write(DataOutput out) throws IOException { // TODO Auto-generated method stub out.writeInt(Min); out.writeInt(Max); } public String toString() { return "min:" + Min + " , max:" + Max; } }
map方法的调用Context内部类的context实例对键值对进行写入操作,该实例包含系统内部的上下文环境,用来存储Map方法产生的输出记录。
Reducer
Reducer函数由Reducer类实现,用于进一步处理map的输出。Reducer除了提供reduce方法外,同样提供了类似于mapper的run(),setup()和cleanup()方法。Reducer的泛型参数[Text,IntWritable,Text,IntWritable]类似与mapper,分别表示Reducer的输入和输出的键值对类型,且Reducer的输入键值对类型[Text,IntWritable]对应于mapper的输出键值对类型。
reduce方法的输入参数是map的输出结果经过MapReducer框架混洗(shuffle)之后的结果,shuffle之后,相同的key会被规约到同一个reduce作业中,所以reduce的参数values是一个key的值列表。
public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> { private IntWritable result = new IntWritable(); public void reduce(Text key, Iterable<IntWritable> values,Context context ) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) { sum += val.get(); } result.set(sum); context.write(key, result); }}
Combiner
Combiner是一个可选的本地reducer,可以在map阶段聚合结果,从而减少map到reduce的网络传输代价,实现mapreduce性能的提升。例如如果同一个map中具有两个”hadoop”单词,如果没有Combiner,则map的输出到reduce接收前网络中会发送两次(“hadoop”,1),而有了Combiner之后,则可以在本地归约为(“hadoop”,2)进行一次网络传输。Combiner函数通常和Reducer的实现一样,但这只是在通常情况下,对于本例的计数是这样,因为在本地的合并不影响最终的结果(a+b = b+a);但是如果计算的是平均值,就不同了,(avg1 + agv2)/2 != 真实的平均值(avg1*m + avg2*n)/(m+n)
任务启动
获取当前系统的环境变量,并据此获得job实例,其中”wordcount”为该job的命名。通过job类可以配置输入/输出的数据格式,跟踪、控制整个任务的执行。
Configuration conf = new Configuration();Job job = Job.getInstance(conf, "wordcount");
setJarByClass()用于设置运行的jar包,hadoop利用传入的参数,查找包含它的jar文件
job.setJarByClass(WordCount.class);
FileInputFormat类的静态方法addInputPath用于新增mapreduce作业的输入目录
FileOutputFormat类的静态方法setOutputPath则定义mapreduce作业的输出保存路径
FileInputFormat.addInputPath(job, new Path(args[0]));FileOutputFormat.setOutputPath(job, new Path(args[1]));
设置map,reduce等处理类:
job.setMapperClass(TokenizerMapper.class);job.setCombinerClass(IntSumReducer.class);job.setReducerClass(IntSumReducer.class);
设置输出的键值对类型:
job.setOutputKeyClass(Text.class);job.setOutputValueClass(IntWritable.class);
提交作业到集群
1.将代码打成jar file,export步骤中在如下的环节为jar选择main class;
2.准备输入数据
可以从本地拷贝任意文件,通过hadoop fs -copyFromLoacl 或者 hadoop fs -put 上传至hdfs
3.提交MR作业
hadoop jar wordcount.jar /import/data/wordcount/ /count
/import/data/wordcount/是待进行计算的输入目录,如果该目录下还有子目录,执行后将会抛出异常
/count是任务的结果路径,如果该路径已经在HDFS上存在,任务将无法提交;也就是说,只需指定结果输出路径,同时确保该路径在当前HDFS上不存在,Reduce会自动创建结果存放路径
4.MapReduce任务调度页面 http://{IP}:8099/
5.控制台输出
# hadoop jar wordcount.jar /import/data/wordcount/ /count17/08/12 11:37:35 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032...17/08/12 11:37:36 INFO input.FileInputFormat: Total input paths to process : 217/08/12 11:37:36 INFO mapreduce.JobSubmitter: number of splits:2...job_1502508379954_0002 running in uber mode : false17/08/12 11:37:46 INFO mapreduce.Job: map 0% reduce 0%17/08/12 11:37:57 INFO mapreduce.Job: map 100% reduce 0%17/08/12 11:38:03 INFO mapreduce.Job: map 100% reduce 100%17/08/12 11:38:05 INFO mapreduce.Job: Job job_1502508379954_0002 completed successfully Map-Reduce Framework ... Shuffle Errors ... File Input Format Counters Bytes Read=156 File Output Format Counters Bytes Written=81# hadoop fs -cat /count/*a 2compute 2data 2framework 2hadoop 2hello 4mapreduce 4mass 2test 2to 2
- Hadoop MapReduce初窥-wordcount示例
- Hadoop MapReduce示例程序WordCount.java手动编译运行解析
- Hadoop MapReduce示例程序WordCount.java手动编译运行解析
- 初学Hadoop之图解MapReduce与WordCount示例分析
- 初学Hadoop之图解MapReduce与WordCount示例分析
- MapReduce入门示例-WordCount
- hadoop mapreduce wordcount编写
- hadoop中的wordcount示例
- hadoop Wordcount示例 出错
- Hadoop入门-WordCount示例
- hadoop示例WordCount
- Hadoop系列二:Hadoop单节点伪分布部署并执行mapreduce示例wordcount
- Hadoop之MapReduce WordCount分析
- Hadoop之MapReduce—Wordcount
- hadoop MapReduce实例解析(WordCount)
- Hadoop MapReduce WordCount程序编写
- hadoop学习(六)WordCount示例深度学习MapReduce过程(1)
- hadoop学习(7)—— 使用yarn运行mapreduce一个简单的wordcount示例
- hdu 6098
- 关于redis
- 六分钟八法则塑造优秀程序员
- AOP的底层实现-CGLIB动态代理和JDK动态代理
- io-RandomAccessFile类
- Hadoop MapReduce初窥-wordcount示例
- windows服务器下配置memcache最大内存值
- I2C总线的简单理解
- 设计模式有什么用
- 李炎恢bootstrap做轮播器的方法与思路
- iOS Objective-C 中是否支持垃圾回收机制
- 暑假训练总结
- BZOJ3770: 疯狂的限制
- 算法题/最大连续子序列和