分析 WordCount 程序.docx

上传人:b****5 文档编号:30061845 上传时间:2023-08-04 格式:DOCX 页数:16 大小:123.91KB
下载 相关 举报
分析 WordCount 程序.docx_第1页
第1页 / 共16页
分析 WordCount 程序.docx_第2页
第2页 / 共16页
分析 WordCount 程序.docx_第3页
第3页 / 共16页
分析 WordCount 程序.docx_第4页
第4页 / 共16页
分析 WordCount 程序.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

分析 WordCount 程序.docx

《分析 WordCount 程序.docx》由会员分享,可在线阅读,更多相关《分析 WordCount 程序.docx(16页珍藏版)》请在冰豆网上搜索。

分析 WordCount 程序.docx

分析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函数时,只需要简单地将其输出的对往OutputCollector中一丢即可,剩余的事框架自会帮你处理好。

代码中LongWritable,IntWritable,Text均是Hadoop中实现的用于封装Java数据类型的类,这些类都能够被串行化从而便于在分布式环境中进行数据交换,你可以将它们分别视为long,int,String的替代品。

Reporter则可用于报告整个应用的运行进度,本例中未使用。

代码清单1

publicstaticclassMapClassextendsMapReduceBase

implementsMapper{

privatefinalstaticIntWritableone=newIntWritable

(1);

privateTextword=newText();

publicvoidmap(LongWritablekey,Textvalue,

OutputCollectoroutput,

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,Iteratorvalues,

OutputCollectoroutput,

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解析成对提供给map函数。

TextInputFormat

(针对文本文件,按行将文本文件切割成InputSplits,并用LineRecordReader将InputSplit解析成对,key是行在文件中的位置,value是文件中的一行)

SequenceFileInputFormat

提供一个RecordWriter的实现,负责输出最终结果

TextOutputFormat

(用LineRecordWriter将最终结果写成纯文件文件,每个对一行,key和value之间用tab分隔)

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,

OutputCollectoroutput,

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模板 > 中国风

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1