MapReduce编程模型及实现WordCount

思想

  • 分而治之
  • map:对每一部分数据进行处理
  • reduce:合并

编程模型

  • 一种分布式计算模型,解决海量数据的计算问题
  • MapReduce将整个并行计算过程抽象到两个函数中
    • map(映射):对一些独立元素组成的列表的每一个元素进行指定的操作,可以高度合并。
    • reduce(化简):对一个列表的元素进行合并
  • 一个简单的MapReduce程序只需要指定map(),reduce(),input和output,剩下的事情由框架完成。

数据传输

input(key-value) ->map(key-value) ->reduce(key-value) ->output(key-value)
整个过程数据传输的过程都是通过key-value形式

编写WordCount程序

首先MapReduce中,map和reduce函数遵循如下常规格式:
map:(k1, V1) -> list:(k2, V2)
reduce:(k2,list( V2)) -> list:(k3, V3)
Mapper的基类:

//继承Mapper类,并重写以下方法
protected void map(KEY inKey, VALUE inValue, 
Context context) throws IOException, InterruptedException
{}

Reducer的基类

//继承Reducer类,并重写以下方法
protected void reduce(KEY inKey, Iterable<VALUE> inValues, 
Context context) throws IOException, InterruptedException
{}

分析WordCount执行过程
1、由于在mapreduce程序中,数据都是以key-value形式进行传输,在读取文件中的数据的时候,会将文件中的数据转换成key-value的形式:
这里写图片描述
如上图所示,最初是将文件中的数据以每一行的数据位value,以每一行的偏移量为key进行存储的,即:<行偏移量, 行数据>,即map方法接受的参数为inKey = 行偏移量, inValue = 行数据。

2、从map中输出的数据也是key-value形式。在map中得到<行偏移量, 行数据>这样的数据后,将数据以行通过”空格”进行分割,将分割后的字符串数组直接遍历输出,此时的key是单词字符串,value为单词的数量(此时输出均为1)。map输出的数据如下所示:
这里写图片描述
3、map过程结束后,mapreduce框架将进行shuffle过程,此过程将进行分组,将相同key的value合并在一起,放到一个集合中,此时shuffle过程会默认按照key进行排序,将合并并排序后的key-values传给reduce方法,传给reduce方法的数据如下:
这里写图片描述
4、reducer得到数据后,只需要将每个key对应的values进行统计输出即可。
这里写图片描述

具体代码实现:

public class WordCount {
    /*
    step1 :map class
    Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
    */
    public static class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
        private Text mapOutputKey = new Text();
        private static final IntWritable mapOutputValue = new IntWritable(1);
        @Override
        public void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
                throws IOException, InterruptedException {
            String line = value.toString();
            //String[] strs = line.split(" ");不使用此方法,由于此方法太消耗内存,而数据量较大
            StringTokenizer stringTokenizer = new StringTokenizer(line);
            while(stringTokenizer.hasMoreTokens()){
                String wordValue = stringTokenizer.nextToken();
                //设置值
                mapOutputKey.set(wordValue);
                //输出
                context.write(mapOutputKey, mapOutputValue);
            }
        }
    }

    //step2 :reduce class
    public static class WordCountReduce extends Reducer<Text, IntWritable, Text, IntWritable>{
        private IntWritable outputValue = new IntWritable();
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values,
                              Context context) throws IOException, InterruptedException {
            //(key values) = (key, list(1, 1, 1))
            int sum = 0;
            for(IntWritable value : values){
                sum += value.get();
            }
            outputValue.set(sum);
            context.write(key, outputValue);
        }
    }

    //step3 :driver
    public  int run(String[] arg0) throws Exception{
        //读取配置文件信息
        Configuration conf = new Configuration();
        //创建Job
        Job job = Job.getInstance(conf, this.getClass().getSimpleName());
        //设置运行的jar
        job.setJarByClass(this.getClass());

        //设置input
        Path inpath = new Path(arg0[0]);
        FileInputFormat.addInputPath(job, inpath);

        //设置map
        job.setMapperClass(WordCountMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //设置reduce
        job.setReducerClass(WordCountReduce.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //设置output
        Path outpath = new Path(arg0[1]);
        FileOutputFormat.setOutputPath(job, outpath);

        //提交job,设置为true会在运行的时候打印日志信息
        boolean isSuccess = job.waitForCompletion(true);
        return isSuccess ? 0 : 1;
    }
    public static void main(String[] args) throws Exception {
        int status = new WordCount().run(args);
        System.exit(status);
    }
}

测试程序

1、将程序打成jar包
2、执行

bin/yarn jar jars/hadooptest.jar com.bxp.hadooptest.mapreduce.WordCount  /user/bxp/mapreduce/wordcount/input /user/bxp/mapreduce/wordcount/output3

3、查看程序运行结果

bin/hdfs dfs -cat /user/bxp/mapreduce/wordcount/output1/part-r-00000

MapReduce程序标准写法

与上面代码区别:
1、extends Configured implements Tool 实现Tool接口的run()方法
2、在main方法中创建Configuration对象
3、在run()方法中通过Configured的get()方法获取main()中创建的Configuration实例对象。

具体代码如下:

public class WordCountMapReduce extends Configured implements Tool{
    /*
    step1 :map class
    Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
    */
    public static class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
        private Text mapOutputKey = new Text();
        private static final IntWritable mapOutputValue = new IntWritable(1);
        @Override
        public void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            String line = value.toString();
            //String[] strs = line.split(" ");不使用此方法,由于此方法太消耗内存,而数据量较大
            StringTokenizer stringTokenizer = new StringTokenizer(line);
            while(stringTokenizer.hasMoreTokens()){
                String wordValue = stringTokenizer.nextToken();
                //设置值
                mapOutputKey.set(wordValue);
                //输出,此处的输出结果通过shuffle过程后传入reduce
                context.write(mapOutputKey, mapOutputValue);
            }
        }
    }

    //step2 :reduce class
    public static class WordCountReduce extends Reducer<Text, IntWritable, Text, IntWritable>{
        private IntWritable outputValue = new IntWritable();
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values,
                              Context context) throws IOException, InterruptedException {
            //(key values) = (key, list(1, 1, 1))
            int sum = 0;
            for(IntWritable value : values){
                sum += value.get();
            }
            outputValue.set(sum);
            context.write(key, outputValue);
        }
    }

    //step3 :driver
    public  int run(String[] arg0) throws Exception{
//        Configuration configuration = new Configuration();
        //读取配置文件信息
        Configuration conf = getConf();
        //创建Job
        Job job = Job.getInstance(conf, this.getClass().getSimpleName());
        //设置运行的jar
        job.setJarByClass(this.getClass());

        //设置input
        Path inpath = new Path(arg0[0]);
        FileInputFormat.addInputPath(job, inpath);

        //设置map
        job.setMapperClass(WordCountMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //设置reduce
        job.setReducerClass(WordCountReduce.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //设置output
        Path outpath = new Path(arg0[1]);
        FileOutputFormat.setOutputPath(job, outpath);

        //提交job,设置为true会在运行的时候打印日志信息
        boolean isSuccess = job.waitForCompletion(true);
        return isSuccess ? 0 : 1;
    }
    public static void main(String[] args) throws Exception {
        Configuration configuration = new Configuration();
//        int status= new WordCountMapReduce().run(args);
        int status = ToolRunner.run(configuration, new WordCountMapReduce(), args);

        System.exit(status);
    }
}

MapReduce程序模版

将具体的业务逻辑留给用户实现,将程序抽取成以下模版,写MapReduce程序时,只需要在//TODO填写具体的业务逻辑即可:

public class MapReduceModel extends Configured implements Tool{

   //TODO  map的输出类型
    public static class ModelMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
        @Override
        public void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
           //TODO     具体的map的业务逻辑

        }
    }

    //step2 :reduce class
    //TODO    reduce的输入输出类型
    public static class ModelReduce extends Reducer<Text, IntWritable, Text, IntWritable>{
        private IntWritable outputValue = new IntWritable();
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values,
                              Context context) throws IOException, InterruptedException {
            //TODO     具体的reduce的业务逻辑
        }
    }

    //step3 :driver
    public  int run(String[] arg0) throws Exception{
//        Configuration configuration = new Configuration();
        //读取配置文件信息
        Configuration conf = getConf();
        //创建Job
        Job job = Job.getInstance(conf, this.getClass().getSimpleName());
        //设置运行的jar
        job.setJarByClass(this.getClass());

        //设置input
        Path inpath = new Path(arg0[0]);
        FileInputFormat.addInputPath(job, inpath);

        //设置map
        //TODO    需要修改ModelMapper
        job.setMapperClass(ModelMapper.class);
        //TODO    需要修改map的输出类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //设置reduce
        //TODO    需要修改ModelReduce
        job.setReducerClass(ModelReduce.class);
        //TODO    需要修改reduce的输出类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //设置output
        Path outpath = new Path(arg0[1]);
        FileOutputFormat.setOutputPath(job, outpath);

        //提交job,设置为true会在运行的时候打印日志信息
        boolean isSuccess = job.waitForCompletion(true);
        return isSuccess ? 0 : 1;
    }
    public static void main(String[] args) throws Exception {
        Configuration configuration = new Configuration();
//        int status= new WordCountMapReduce().run(args);
        int status = ToolRunner.run(configuration, new MapReduceModel(), args);

        System.exit(status);
    }
}

模版优化:
Mapper类和Reducer类中。可以去实现父类的setup()和cleanup()方法

  protected void setup(Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
    }
    protected void cleanup(Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
    }

此两个方法分别是在map和reduce方法执行之前和之后去执行,比如创建数据库的链接和链接的释放可以在这两个方法中执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值