分析 WordCount 程序.docx
- 文档编号:30061845
- 上传时间:2023-08-04
- 格式:DOCX
- 页数:16
- 大小:123.91KB
分析 WordCount 程序.docx
《分析 WordCount 程序.docx》由会员分享,可在线阅读,更多相关《分析 WordCount 程序.docx(16页珍藏版)》请在冰豆网上搜索。
分析WordCount程序
分析WordCount程序
我们先来看看Hadoop自带的示例程序WordCount,这个程序用于统计一批文本文件中单词出现的频率,完整的代码可在下载的Hadoop安装包中得到(在src/examples目录中)。
1.实现Map类
见代码清单1。
这个类实现Mapper接口中的map方法,输入参数中的value是文本文件中的一行,利用StringTokenizer将这个字符串拆成单词,然后将输出结果<单词,1>写入到org.apache.hadoop.mapred.OutputCollector中。
OutputCollector由Hadoop框架提供,负责收集Mapper和Reducer的输出数据,实现map函数和reduce函数时,只需要简单地将其输出的
代码中LongWritable,IntWritable,Text均是Hadoop中实现的用于封装Java数据类型的类,这些类都能够被串行化从而便于在分布式环境中进行数据交换,你可以将它们分别视为long,int,String的替代品。
Reporter则可用于报告整个应用的运行进度,本例中未使用。
代码清单1
publicstaticclassMapClassextendsMapReduceBase
implementsMapper
privatefinalstaticIntWritableone=newIntWritable
(1);
privateTextword=newText();
publicvoidmap(LongWritablekey,Textvalue,
OutputCollector
Reporterreporter)throwsIOException{
Stringline=value.toString();
StringTokenizeritr=newStringTokenizer(line);
while(itr.hasMoreTokens()){
word.set(itr.nextToken());
output.collect(word,one);
}
}
}
2.实现Reduce类
见代码清单2。
这个类实现Reducer接口中的reduce方法,输入参数中的key,values是由Map任务输出的中间结果,values是一个Iterator,遍历这个Iterator,就可以得到属于同一个key的所有value.此处,key是一个单词,value是词频。
只需要将所有的value相加,就可以得到这个单词的总的出现次数。
代码清单2
publicstaticclassReduceextendsMapReduceBase
implementsReducer
publicvoidreduce(Textkey,Iterator
OutputCollector
Reporterreporter)throwsIOException{
intsum=0;
while(values.hasNext()){
sum+=values.next().get();
}
output.collect(key,newIntWritable(sum));
}
}
3.运行Job
在Hadoop中一次计算任务称之为一个job,可以通过一个JobConf对象设置如何运行这个job。
此处定义了输出的key的类型是Text,value的类型是IntWritable,指定使用代码清单1中实现的MapClass作为Mapper类, 使用代码清单2中实现的Reduce作为Reducer类和Combiner类,任务的输入路径和输出路径由命令行参数指定,这样job运行时会处理输入路径下的所有文件,并将计算结果写到输出路径下。
然后将JobConf对象作为参数,调用JobClient的runJob,开始执行这个计算任务。
至于main方法中使用的ToolRunner是一个运行MapReduce任务的辅助工具类,依样画葫芦用之即可。
代码清单3
publicintrun(String[]args)throwsException{
JobConfconf=newJobConf(getConf(),WordCount.class);
conf.setJobName("wordcount");
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(IntWritable.class);
conf.setMapperClass(MapClass.class);
conf.setCombinerClass(Reduce.class);
conf.setReducerClass(Reduce.class);
conf.setInputPath(newPath(args[0]));
conf.setOutputPath(newPath(args[1]));
JobClient.runJob(conf);
return0;
}
publicstaticvoidmain(String[]args)throwsException{
if(args.length!
=2){
System.err.println("Usage:
WordCount
System.exit(-1);
}
intres=ToolRunner.run(newConfiguration(),newWordCount(),args);
System.exit(res);
}
}
以上就是WordCount程序的全部细节,简单到让人吃惊,您都不敢相信就这么几行代码就可以分布式运行于大规模集群上,并行处理海量数据集。
4.通过JobConf定制计算任务
通过上文所述的JobConf对象,程序员可以设定各种参数,定制如何完成一个计算任务。
这些参数很多情况下就是一个java接口,通过注入这些接口的特定实现,可以定义一个计算任务(job)的全部细节。
了解这些参数及其缺省设置,您才能在编写自己的并行计算程序时做到轻车熟路,游刃有余,明白哪些类是需要自己实现的,哪些类用Hadoop的缺省实现即可。
表一是对JobConf对象中可以设置的一些重要参数的总结和说明,表中第一列中的参数在JobConf中均会有相应的get/set方法,对程序员来说,只有在表中第三列中的缺省值无法满足您的需求时,才需要调用这些set方法,设定合适的参数值,实现自己的计算目的。
针对表格中第一列中的接口,除了第三列的缺省实现之外,Hadoop通常还会有一些其它的实现,我在表格第四列中列出了部分,您可以查阅Hadoop的API文档或源代码获得更详细的信息,在很多的情况下,您都不用实现自己的Mapper和Reducer,直接使用Hadoop自带的一些实现即可。
/*这个是很重要的表格,明白了这几个类的使用之后,要了解上面的wordcount程序就不难了*/
表一JobConf常用可定制参数
将输入的数据集切割成小数据集InputSplits,每一个InputSplit将由一个Mapper负责处理。
此外InputFormat中还提供一个RecordReader的实现,将一个InputSplit解析成
TextInputFormat
(针对文本文件,按行将文本文件切割成InputSplits,并用LineRecordReader将InputSplit解析成
SequenceFileInputFormat
提供一个RecordWriter的实现,负责输出最终结果
TextOutputFormat
(用LineRecordWriter将最终结果写成纯文件文件,每个
SequenceFileOutputFormat
输出的最终结果中key的类型
LongWritable
输出的最终结果中value的类型
Text
Mapper类,实现map函数,完成输入的
IdentityMapper
(将输入的
LongSumReducer,
LogRegexMapper,
InverseMapper
实现combine函数,将中间结果中的重复key做合并
null
(不对中间结果中的重复key做合并)
Reducer类,实现reduce函数,对中间结果做合并,形成最终结果
IdentityReducer
(将中间结果直接输出为最终结果)
AccumulatingReducer,LongSumReducer
设定job的输入目录,job运行时会处理输入目录下的所有文件
null
设定job的输出目录,job的最终结果会写入输出目录下
null
设定map函数输出的中间结果中key的类型
如果用户没有设定的话,使用OutputKeyClass
设定map函数输出的中间结果中value的类型
如果用户没有设定的话,使用OutputValuesClass
对结果中的key进行排序时的使用的比较器
WritableComparable
对中间结果的key排序后,用此Partition函数将其划分为R份,每份由一个Reducer负责处理。
HashPartitioner
(使用Hash函数做partition)
KeyFieldBasedPartitionerPipesPartitioner
回页首
改进的WordCount程序
现在你对Hadoop并行程序的细节已经有了比较深入的了解,我们来把WordCount程序改进一下,目标:
(1)原WordCount程序仅按空格切分单词,导致各类标点符号与单词混杂在一起,改进后的程序应该能够正确的切出单词,并且单词不要区分大小写。
(2)在最终结果中,按单词出现频率的降序进行排序。
1.修改Mapper类,实现目标
(1)
实现很简单,见代码清单4中的注释。
代码清单4
publicstaticclassMapClassextendsMapReduceBase
implementsMapper
privatefinalstaticIntWritableone=newIntWritable
(1);
privateTextword=newText();
privateStringpattern="[^\\w]";//正则表达式,代表不是0-9,a-z,A-Z的所有其它字符
publicvoidmap(LongWritablekey,Textvalue,
OutputCollector
Reporterreporter)throwsIOException{
Stringline=value.toString().toLowerCase();//全部转为小写字母
line=line.replaceAll(pattern,"");//将非0-9,a-z,A-Z的字符替换为空格
StringTokenizeritr=newStringTokenizer(line);
while(itr.hasMoreTokens()){
word.set(itr.nextToken());
output.collect(word,one);
}
}
}
2.实现目标
(2)
用一个并行计算任务显然是无法同时完成单词词频统计和排序的,这时我们可以利用Hadoop的任务管道能力,用上一个任务(词频统计)的输出做为下一个任务(排序)的输入,顺序执行两个并行计算任务。
主要工作是修改代码清单3中的run函数,在其中定义一个排序任务并运行之。
在Hadoop中要实现排序是很简单的,因为在MapReduce的过程中,会把中间结果根据key排序并按key切成R份交给R个Reduce函数,而Reduce函数在处理中间结果之前也会有一个按key进行排序的过程,故MapReduce输出的最终结果实际上已经按key排好序。
词频统计任务输出的key是单词,value是词频,为了实现按词频排序,我们指定使用InverseMapper类作为排序任务的Mapper类(sortJob.setMapperClass(InverseMapper.class);),这个类的map函数简单地将输入的key和value互换后作为中间结果输出,在本例中即是将词频作为key,单词作为value输出,这样自然就能得到按词频排好序的最终结果。
我们无需指定Reduce类,Hadoop会使用缺省的IdentityReducer类,将中间结果原样输出。
还有一个问题需要解决:
排序任务中的Key的类型是IntWritable,(sortJob.setOutputKeyClass(IntWritable.class)),Hadoop默认对IntWritable按升序排序,而我们需要的是按降序排列。
因此我们实现了一个IntWritableDecreasingComparator类, 并指定使用这个自定义的Comparator类对输出结果中的key(词频)进行排序:
sortJob.setOutputKeyComparatorClass(IntWritableDecreasingComparator.class)
详见代码清单5及其中的注释。
代码清单5
publicintrun(String[]args)throwsException{
PathtempDir=newPath("wordcount-temp-"+Integer.toString(
newRandom().nextInt(Integer.MAX_VALUE)));//定义一个临时目录
JobConfconf=newJobConf(getConf(),WordCount.class);
try{
conf.setJobName("wordcount");
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(IntWritable.class);
conf.setMapperClass(MapClass.class);
conf.setCombinerClass(Reduce.class);
conf.setReducerClass(Reduce.class);
conf.setInputPath(newPath(args[0]));
conf.setOutputPath(tempDir);//先将词频统计任务的输出结果写到临时目
//录中,下一个排序任务以临时目录为输入目录。
conf.setOutputFormat(SequenceFileOutputFormat.class);
JobClient.runJob(conf);
JobConfsortJob=newJobConf(getConf(),WordCount.class);
sortJob.setJobName("sort");
sortJob.setInputPath(tempDir);
sortJob.setInputFormat(SequenceFileInputFormat.class);
/*InverseMapper由hadoop库提供,作用是实现map()之后的数据对的key和value交换*/
sortJob.setMapperClass(InverseMapper.class);
sortJob.setNumReduceTasks
(1);//将Reducer的个数限定为1,最终输出的结果
//文件就是一个。
sortJob.setOutputPath(newPath(args[1]));
sortJob.setOutputKeyClass(IntWritable.class);
sortJob.setOutputValueClass(Text.class);
sortJob.setOutputKeyComparatorClass(IntWritableDecreasingComparator.class);
JobClient.runJob(sortJob);
}finally{
FileSystem.get(conf).delete(tempDir); //删除临时目录
}
return0;
}
privatestaticclassIntWritableDecreasingComparatorextendsIntWritable.Comparator{
publicintcompare(WritableComparablea,WritableComparableb){
return-pare(a,b);
}
/*这个比较是什么意思?
*/
publicintcompare(byte[]b1,ints1,intl1,byte[]b2,ints2,intl2){
return-pare(b1,s1,l1,b2,s2,l2);
}
}
回页首
在Eclipse环境下进行开发和调试
在Eclipse环境下可以方便地进行Hadoop并行程序的开发和调试。
推荐使用IBMMapReduceToolsforEclipse,使用这个Eclipseplugin可以简化开发和部署Hadoop并行程序的过程。
基于这个plugin,可以在Eclipse中创建一个HadoopMapReduce应用程序,并且提供了一些基于MapReduce框架的类开发的向导,可以打包成JAR文件,部署一个HadoopMapReduce应用程序到一个Hadoop服务器(本地和远程均可),可以通过一个专门的视图(perspective)查看Hadoop服务器、Hadoop分布式文件系统(DFS)和当前运行的任务的状态。
可在IBMalphaWorks网站下载这个MapReduceTool,或在本文的下载清单中下载。
将下载后的压缩包解压到你Eclipse安装目录,重新启动Eclipse即可使用了。
设置Hadoop主目录
点击Eclipse主菜单上Windows->Preferences,然后在左侧选择HadoopHomeDirectory,设定你的Hadoop主目录,如图一所示:
图1
创立一个MapReduceProject
点击Eclipse主菜单上File->New->Project,在弹出的对话框中选择MapReduceProject,输入projectname如wordcount, 然后点击Finish即可。
如图2所示:
图2
此后,你就可以象一个普通的EclipseJavaproject那样,添加入Java类,比如你可以定义一个WordCount类,然后将本文代码清单1,2,3中的代码写到此类中,添加入必要的import语句(Eclipse快捷键ctrl+shift+o可以帮你),即可形成一个完整的wordcount程序。
在我们这个简单的wordcount程序中,我们把全部的内容都放在一个WordCount类中。
实际上IBMMapReducetools还提供了几个实用的向导(wizard)工具,帮你创建单独的Mapper类,Reducer类,MapReduceDriver类(就是代码清单3中那部分内容),在编写比较复杂的MapReduce程序时,将这些类独立出来是非常有必要的,也有利于在不同的计算任务中重用你编写的各种Mapper类和Reducer类。
在Eclipse中运行
如图三所示,设定程序的运行参数:
输入目录和输出目录之后,你就可以在Eclipse中运行wordcount程序了,当然,你也可以设定断点,调试程序。
图3
回页首
结束语
到目前为止,我们已经介绍了MapReduce计算模型,分布式文件系统HDFS,分布式并行计算等的基本原理,如何安装和部署单机Hadoop环境,实际编写了一个Hadoop并行计算程序,并了解了一些重要的编程细节,了解了如何使用IBMMapReduceTools在Eclipse环境中编译,运行和调试你的Hadoop并行计算程序。
但一个Hadoop并行计算程序,只有部署运行在分布式集群环境中,才能发挥其真正的优势,在这篇系列文章的第3部分中,你将了解到如何部署你的分布式Hadoop环境,如何利用IBMMapReduceTools将你的程序部署到分布式环境中运行等内容。
阅读总结:
mapredu
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 分析 WordCount 程序
![提示](https://static.bdocx.com/images/bang_tan.gif)