mapreduce(1)--wordcount的实现

1.需求

利用mapreduce编程框架编写wordcount程序。

2.环境配置

(1)hadoop为本地模式

(2)pom文件代码如下

<dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.3</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
 </dependencies>
View Code

 3.mapreduce介绍

(1)mapreduce结构

完整的mapreduce在分布式运行时有三类实例:MRAppMaster,MapTask,ReduceTask.

  • MRAppMaster是负责整个程序过程调度以及状态协调,会根据需要创建一定数量的MapTask和ReduceTask
  • MapTask是负责map阶段的整个数据处理,MapTask一般是对一行调用一次用户自定义的map函数
  • ReduceTask是负责reduce阶段的整个数据处理,ReduceTask一般是对一组<k,v>调用一次用户自定义的reduce函数

(2)流程解析

  • 执行用户的Driver类中的main函数,向MRAppMaster提交job
  • MRAppMaster启动后决定maptask实例数量,进而启动相应数量的maptask进程。(具体如何分配参见(3))
  • maptask启动之后,一般是对一行调用一次用户自定义的map函数
  • MRAppMaster在等待所有的maptask进程完成之后,会启动相应数量的reducetask进程,并给每个reducetask指定数据范围。
  • reducetask启动之后,获取maptask的输出结果,并按照相同key为一组,对每一组调用一次我们重写的reduce方法。处理完成后向外部文件写结果。

(3)maptask的数量如何确定

最根本的因素:每个split分配一个maptask,也就是每个切片对应一个map任务

切片大小可以指定,但是最好和hdfs上的block大小一致(默认情况就是一致),这是因为如果不相等,maptask需要从其他datanode通过网络传输数据,这样就会受带宽限制。

(4)如果文件大小小于blocksize,那么split大小是block大小还是文件大小?

在hdfs上存储的文件如果大小小于blocksize,那么在hdfs上占用的空间是文件大小,blocksize的作用是在追加文件内容的时候决定何时进行划分(超过blocksize就要将文件分为两个block)。

这样总结起来,如果文件大小小于blocksize的部分,那么对应的split就是该部分文件的大小,其余部分是等于blocksize的。

4.wordcount程序介绍

在mapreduce框架下实现wordcount,需要自定义一个WordcountMapper继承Mapper并重写map方法自定义一个WordcountReducer继承Reducer并重写reduce方法,以及程序的main方法WordcountDriver。

5.wordcount代码具体实现

(1)WordcountMapper

//1.定义四个泛型类型:KEYIN:LongWritable,VALUEIN:Text,
// KEYOUT:Text, VALUEOUT:IntWritable
//各个泛型类型与java中的对比:LongWritable->Long
//Text->String,IntWritable->Integer

//2.这里之所以定义这些泛型类型,是因为网络传输需要序列化,精简java中的序列化接口

//3.关于四个泛型作用:
// KEYIN(LongWritable类型):一行文本的开始位置,在整个文本的偏移量(用来确定当前是哪一行的,对于业务来说没有用)
// VALUEIN(Text类型):读到一行文本的内容,使用这个文本划分成单词
// KEYOUT(Text类型):输出的单词
// VALUEOUT(IntWritable类型):单词的统计次数

public
class WordcountMapper extends Mapper<LongWritable, Text,Text, IntWritable> { //map阶段的业务逻辑,被maptask调用 @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //将传入的数据按空格分割成单词 String[] words = value.toString().split(" "); //将单词输出为<单词,1> for(String word:words){ //将单词作为key,将次数1作为value //根据单词的分发,相同的key会进入相同的reduce task中 //context是mr框架提供的上下文 //还注意要使用Text,IntWritable类型 context.write(new Text(word),new IntWritable(1)); } } }

(2)WordcountReducer类

//KEYINVALUEIN对应mapper输出的KEYOUTVALUEOUT
//KEYOUTVALUEOUT是自定义的
//KEYOUT是单词,VALUEOUT是单词的总个数

public
class WordcountReducer extends Reducer<Text, IntWritable,Text,IntWritable> { //reduce函数会被reduceTask任务调用,每一组单词调用一次 //每一组的意思就是[<hello,1>,<hello,1>,<hello,1>]... @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int count = 0; for(IntWritable value:values){ //value表示从迭代器中取出的值,是统计的个数,要使用.get()转换函数转换成int类型 count += value.get(); } //key是单词,new IntWritable(count)是单词的总个数 //其实context是将结果写入文件当中,文件存储在hdfs上 context.write(key,new IntWritable(count)); } }

 (3)WordcountDriver

public class WordcountDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration configuration = new Configuration();
        Job job =  Job.getInstance(configuration);

        //指定本程序jar包所在的路径
        job.setJarByClass(WordcountDriver.class);

        //利用反射指定job要使用的mapper业务类
        job.setMapperClass(WordcountMapper.class);
        job.setReducerClass(WordcountReducer.class);

        //利用反射,指定mapper输出数据的kv类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //设置最终(也就是reduce)输出的数据类型
        job.setOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //指定job输入原始文件所在目录
        FileInputFormat.setInputPaths(job,new Path(args[0]));
        //指定job的输出结果
        FileOutputFormat.setOutputPath(job,new Path(args[1]));

        //将job中配置的相关参数,提交给yarn运行
        //等待集群完成工作
        boolean res = job.waitForCompletion(true);
        System.exit(res?0:1);
    }
}

6.打成jar包并运行

  • 打成jar包的操作步骤详见:https://blog.csdn.net/sinat_33201781/article/details/80264828
  • 运行命令:hadoop jar [jar包路径] [主类的相对路径]  [输入文件在hdfs上的路径] [输出文件在hdfs上的路径]例如:hadoop jar /home/zhangjiaqian/IdeaProjects/STBigData/target/ChuanzhiBigdata-1.0-SNAPSHOT.jar cn.gulu.bigdata.mr.MRDemos.WordcountDriver /wordcount/input /wordcount/output
  • 运行过后可在hdfs上查看输出文件
  • 查看输出内容:hdfs dfs -cat /wordcount/output/part-r-00000

7.github链接:

  https://github.com/gulu2016/STBigData/tree/master/src/main/java/cn/gulu/bigdata/mr/MRDemos

8.练习:使用mr进行流量统计

   github链接:https://github.com/gulu2016/STBigData/tree/master/src/main/java/cn/gulu/bigdata/mr/flowsum

9.练习2:在8的基础上对手机号以身份进行划分,即不同省份的手机号进入不同的reducetask中

  这里边最主要的任务就是重写Partitioner中的getPartition方法,使相同省份的手机号获得同一个数字

  github链接:https://github.com/gulu2016/STBigData/tree/master/src/main/java/cn/gulu/bigdata/mr/flowProvinceSum

10.练习3:在8的基础上对根据总流量从高到低进行排序

  这里需要考虑的是选取哪个值作为maptask的key,以及如何对key进行排序

  github链接:https://github.com/gulu2016/STBigData/tree/master/src/main/java/cn/gulu/bigdata/mr/flowCountSort

posted @ 2019-05-01 12:05  HelloNewCoder  阅读(973)  评论(0编辑  收藏  举报