MapReduce代码实现1


三:MapReduce 是一种分布式计算模型。
MapReduce代码实现1_第1张图片
Mapreduce 框架有默认实现,程序员只需要覆盖map() reduce() 两个函数。
MapReduce 的执行流程
1.Map Task (以一个入门例子的单词计数为例,两行一定行是hello word 第二行是hello you 中间是制表符)
         1.1 读取:框架调用InputFormat 类的子类读取HDFS 中文件数据,把文件转换为InputSplit 。默认,文件的一个block 对应一个InputSplit ,一个InputSplit 对应一个map task
             一个InputSplit 中的数据会被RecordReader 解析成 。默认,InputSplit 中的一行解析成一个 。默认,v1 表示一行的内容,k1 表示偏移量。读取的结果是<0,helloword> <10,hello you> 10 是第二行的起始偏移量,这两个是
         1.2map :框架调用Mapper 类中的map(k1,v1) 方法,接收 ,输出 。有多少个 map() 会被执行多少次。输出
                   程序员可以覆盖map() ,实现自己的业务逻辑。
         1.3 分区:框架对map 的输出进行分区。分区的目的是确定哪些 进入哪个reduce task 。默认,只有一个分区。可以手动设置(0 1 2 等后面会涉及到)
         1.4 排序分组:框架对不同分区中的 进行排序、分组。
             排序是按照k2 进行排序。结果是
                   分组指的是相同k2 v2 分到一个组中。分组不会减少 的数量。
         1.5combiner :可以在map task 中对 执行reduce 归约。,1}>
         1.6 写入本地:框架对map 的输出写入到linux 本地磁盘。
2.Reduce Task
         2.1shuffle :框架根据map 不同的分区中的数据,通过网络copy 到不同的reduce 节点。
         2.2 合并排序分组:每个reduce 会把多个map 传来的 进行合并、排序、分组。
         2.3reduce :框架调用reduce(k2,v2s) 。有多少个分组,就会执行多少次reduce 函数。
         2.4 写入HDFS :框架对reduce 的输出写入到HDFS 中。

单词计数代码示例

package mp.wordcount;

import java.io.IOException;

importorg.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
*
*--------------------------------------------
* 原始数据
*hello    you
*hello    me
*---------------------
* 结果如下
*hello    2
* me       1
*you      1
*
*-------------------------------------------------------------
*
*
*/
public class HelloWordCountApp2 {
         /**
          * 驱动代码
          * @param args
          * @throws Exception
          */
         publicstatic void main(String[] args) throws Exception {
                   // 从命令行传入输入路径
                   StringinputPath = args[0];
                   // 从命令行传入输出目录
                   PathoutputDir = new Path(args[1]);


                   Configurationconf = new Configuration();
                   // 表示job 名称,可以自定义,一般是类名
                   StringjobName = HelloWordCountApp2.class.getSimpleName();
                   // 把所有的相关内容都封装到job
                   Jobjob = Job.getInstance(conf, jobName);
                   // 打成jar 运行必备代码
                   job.setJarByClass(HelloWordCountApp2.class);

                   // 设置输入路径
                   FileInputFormat.setInputPaths(job,inputPath);
                   // 设置输出目录
                   FileOutputFormat.setOutputPath(job,outputDir);

                   // 设置自定义mapper
                   job.setMapperClass(HelloWordCountMapper.class);
                   // 指定k2,v2 类型
                   job.setMapOutputKeyClass(Text.class);
                   job.setMapOutputValueClass(LongWritable.class);


                   // 设置自定义reduce
                   job.setReducerClass(HelloWordCountReducer.class);
                   // 指定k3 v3 类型
                   job.setOutputKeyClass(Text.class);
                   job.setOutputValueClass(LongWritable.class);

                   // 提交给yarn 运行,等待结束
                   job.waitForCompletion(true);
         }



         /**
          * map 过程。
          * 在这里,程序员继承Mapper ,覆盖map(...) 方法。
          * 该类在运行的时候,称作map task ,是一个java 进程。
          *----------------------------------------------------
          * map() 全部执行完后,产生的 4 个,即
          * 排序后是
          * 分组后是
          *
          *
          */
         publicstatic class HelloWordCountMapper extends Mapper{
                   Textk2 = new Text();
                   LongWritablev2 = new LongWritable();

                   /**
                    * 前面已经有拆分完成的 。调用map() 一次方法,就处理一个 对。
                    *
                    * map() 方法,拆分每一行,得到每个单词,每个单词( 不是每个不同的单词) 的出现次数是1
                    * 构造 k2 表示单词,v2 表示出现次数1
                    */
                   @Override
                   protectedvoid map(LongWritable key, Text value, Mapper.Context context)
                                     throwsIOException, InterruptedException {
                            // 因为要对每行内容做拆分,需要调用String.split() ,所以需要把Text 转行成String
                            Stringline = value.toString();
                            // 拆分每行内容,结果是单词的数组
                            String[]splited = line.split("\t");
                            // 循环数组,取每个单词。在for 循环中构造
                            for(String word : splited) {
                                     k2.set(word);
                                     v2.set(1L);
                                     // 写出去,相当于调用return 语句
                                     context.write(k2,v2);
                            }
                   }
         }


         /**
          * reduce 过程
          *
          * reduce 端接收的是map 的输出,即4 3 个分组。
          * reduce 执行之前,reduce 端合并、排序、分组
          * reduce() 调用之前,有3 个分组,即
          * 一次reduce() 执行,处理1 个分组。所以说,执行3 reduce()
          * ------------------------------------------------------------------
          * reduce task 执行结束后,框架会把reduce 输出的 写入到HDFS
          *
          */
         publicstatic class HelloWordCountReducer extends Reducer{
                   LongWritablev3 = new LongWritable();

                   /**
                    * k2 表示每个不同的单词
                    * v2s 表示每个不同的单词的出现次数
                    * reduce() 中,只需要汇总v2s 中的出现次数就行。
                    */
                   @Override
                   protectedvoid reduce(Text k2, Iterable v2s,
                                     Reducer.Context context) throws IOException,InterruptedException {
                            //sum 表示当前单词k2 出现的总次数
                            longsum = 0L;
                            for(LongWritable v2 : v2s) {
                                     sum+= v2.get();
                            }
                            //k3 表示当前不同的单词,与k2 含义相同

                            v3.set(sum);
                            context.write(k2,v3);
                   }
         }
}

打成jar 包在hdfs 中执行yarn jar jar 包名 /hello /out
         /hello 就是要统计的单词文本,是上传到hdfs 上的 /out 输出路径,如果已存在可以删除,也可在代码中删除
执行结果:
MapReduce代码实现1_第2张图片


代码中用到序列化
Hadoop 的序列化格式:Writable
hadoop 序列化的目的是什么?
mapreduce 运行过程中,产生大量的磁盘io 和网络io 。序列化性能的差异,会对job 的运行效率产生非常大的影响。因此,高效率的序列化机制可以提高效率。




部分代码类方法解析
1.InputFormat 里面有2 个方法,一个是getSplits() ,一个是createRecordReader()
    在执行mapreduce 之前,原始数据被分割成若干split ,每个split 作为一个
map任务的输入。每一个InputSplit都有一个RecordReader,作用是把InputSplit中的数据解析成Record,即.TextInputFormat中的RecordReader是LineRecordReader,每一行解析成一个。其中,k1表示偏移量,v1表示行文本内容
FileInputFormat 类中分析了getSplits()
TextInputFormat 类是MR 默认的输入处理类。主要分析的是LineRecordReader
Maper 类的源代码中,有setup cleanup map run 四个重要的方法。

2.SequenceFileInputFormat 专门处理类型是SequenceFile 格式的输入文件。
   如果是大量的小文件作为输入文件,那么会产生大量的map task
   如果把大量的小文件转换为SequenceFile 格式,那么会产生非常少的maptask
   如果SequenceFile 使用压缩,那么maptask 执行时间会更短。

job.setInputFormatClass(SequenceFileInputFormat.class);

3.CombineFileInputFormat 作用是把大量的小文件交给一个map task   
   在这里,输入依然是小文件,但是会由非常少的map task 运行。

job.setInputFormatClass(CombineSmallFilesInputFormat.class);
--------------------------------------------------------------------------------------
4.OutputFormat 里面有个很重要的类,叫做RecordWriter
5. 使用MultipleOutputs 可以自定义输出的文件名。
---------------------------------------------------------------------------------------
6. map task 或者reduce task 中使用第三方的jar 包。

   首先把第三方jar 包上传到hdfs 中,然后调用job.addArchiveToClasspath(...)  

7. 分区partitioiner
   默认的分区是1 个,分区的实现是HashPartitioner
    什么时候用到分区?当需要把不同的数据按照不同的类型输出时,使用分区,例如不同的省份的安电话区号分别输出到不同的文件中。自定义分区类继承 HashPartitioner 例如extends HashPartitioner   覆盖 getPartition   方法(方法内根据业务实现分区)。

job.setPartitionClass(.....)
job.setNumReduceTasks(...)

8. 归约combiner
   发生在map 端的reduce 操作。
   作用是减少map 端的输出,减少shuffle 过程中网络传输的数据量,提高作业的执行效率。
combiner 仅仅是单个map task reduce ,没有对全部map 的输出做reduce
job.setCombinerClass(....)  

9. 排序sort (见下面代码)
   两种比较方式,一种是调用k2 compareTo(...) 完成比较,第二种是自定义类extendsWritableComparator
job.setSortComparatorClass(....)

   注意:extendsWritableComparator 的子类一定要有个无参构造方法,在该构造方法中,调用父类的有2 个参数的构造方法。

10. 分组grouping (例如日志采集中不同主机的操作记录,可以按照主机ip 分组做其他处理)
    当排序逻辑与分组逻辑不一样时,就需要自定义分组。自定义类extends WritableComparator
  job.setGroupingComparatorClass(...)


对于排序 示例代码
例如需求对列排序如果第一行相同按照第二行大小排序
1   2
2   1
1   1
2   2
1       3
要求输出结果
1   1
1   2
1       3
2       1
2       2
思路 一将第一列数和第二列数 封装到自定义的一个类TwoInt 中进行
自定义排序CustomSortComparator extends WritableComparator
思路二按照 k2 排序
方法1
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

importorg.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
importorg.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
* 利用k2 compareTo() 实现排序
*
*
*/
public class SortApp1 {
         /**
          * 驱动代码
          * @param args
          * @throws Exception
          */
         publicstatic void main(String[] args) throws Exception {
                   // 从命令行传入输入路径
                   StringinputPath = args[0];
                   // 从命令行传入输出目录
                   PathoutputDir = new Path(args[1]);
                   //reduce 数量
                   IntegernumReduceTasks = Integer.parseInt(args[2]==null?"1":args[2]);



                   Configurationconf = new Configuration();
                   outputDir.getFileSystem(conf).delete(outputDir,true);
                   // 表示job 名称,可以自定义,一般是类名
                   StringjobName = SortApp1.class.getSimpleName();
                   // 把所有的相关内容都封装到job
                   Jobjob = Job.getInstance(conf, jobName);
                   // 打成jar 运行必备代码
                   job.setJarByClass(SortApp1.class);

                   // 设置输入路径
                   FileInputFormat.setInputPaths(job,inputPath);
                   // 设置输出目录
                   FileOutputFormat.setOutputPath(job,outputDir);

                   // 设置自定义mapper
                   job.setMapperClass(SortMapper.class);
                   // 指定k2,v2 类型
                   job.setMapOutputKeyClass(TwoInt.class);
                   job.setMapOutputValueClass(NullWritable.class);

                   job.setNumReduceTasks(numReduceTasks);

                   // 设置自定义reduce
                   job.setReducerClass(SortReducer.class);
                   // 指定k3 v3 类型
                   job.setOutputKeyClass(TwoInt.class);
                   job.setOutputValueClass(NullWritable.class);

                   // 提交给yarn 运行,等待结束
                   job.waitForCompletion(true);
         }


         publicstatic class SortMapper extends Mapper{
                   TwoIntk2 = new TwoInt();
                   @Override
                   protectedvoid map(LongWritable key, Text value,
                                     Mapper.Context context)
                                                        throwsIOException, InterruptedException {
                            Stringline = value.toString();
                            String[]splited = line.split("\t");

                            k2.set(Integer.parseInt(splited[0]),Integer.parseInt(splited[1]));
                            context.write(k2,NullWritable.get());
                   }
         }

         publicstatic class SortReducer extends Reducer{
                   @Override
                   protectedvoid reduce(TwoInt k2, Iterable v2s,
                                     Reducer.Context context)
                                                        throwsIOException, InterruptedException {
                            context.write(k2,NullWritable.get());
                   }
         }


         publicstatic class TwoInt implements WritableComparable{
                   privateInteger first;
                   privateInteger second;

                   publicTwoInt() {
                            super();
                   }

                   publicvoid set(Integer first, Integer second) {
                            this.first= first;
                            this.second= second;
                   }

                   publicvoid write(DataOutput out) throws IOException {
                            out.writeInt(this.first);
                            out.writeInt(this.second);
                   }

                   publicvoid readFields(DataInput in) throws IOException {
                            this.first=in.readInt();
                            this.second= in.readInt();
                   }

                   publicint compareTo(TwoInt o) {
                            intret1 = first.compareTo(o.getFirst());
                            if(ret1==0){
                                     returnsecond.compareTo(o.getSecond());
                            }else{
                                     returnret1;
                            }
                   }

                   publicInteger getFirst() {
                            returnfirst;
                   }

                   publicvoid setFirst(Integer first) {
                            this.first= first;
                   }

                   publicInteger getSecond() {
                            returnsecond;
                   }

                   publicvoid setSecond(Integer second) {
                            this.second= second;
                   }

                   @Override
                   publicint hashCode() {
                            finalint prime = 31;
                            intresult = 1;
                            result= prime * result + ((first == null) ? 0 : first.hashCode());
                            result= prime * result + ((second == null) ? 0 : second.hashCode());
                            returnresult;
                   }

                   @Override
                   publicboolean equals(Object obj) {
                            if(this == obj)
                                     returntrue;
                            if(obj == null)
                                     returnfalse;
                            if(getClass() != obj.getClass())
                                     returnfalse;
                            TwoIntother = (TwoInt) obj;
                            if(first == null) {
                                     if(other.first != null)
                                               returnfalse;
                            }else if (!first.equals(other.first))
                                     returnfalse;
                            if(second == null) {
                                     if(other.second != null)
                                               returnfalse;
                            }else if (!second.equals(other.second))
                                     returnfalse;
                            returntrue;
                   }

                   @Override
                   publicString toString() {
                            returnfirst + "\t" + second;
                   }

         }

}

方法2

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

importorg.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
importorg.apache.hadoop.io.WritableComparable;
importorg.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
* 自定义sort 类,实现k2 的排序
*
*
*/
public class SortApp2 {
         /**
          * 驱动代码
          * @param args
          * @throws Exception
          */
         publicstatic void main(String[] args) throws Exception {
                   // 从命令行传入输入路径
                   StringinputPath = args[0];
                   // 从命令行传入输出目录
                   PathoutputDir = new Path(args[1]);
                   //reduce 数量
                   IntegernumReduceTasks = Integer.parseInt(args[2]==null?"1":args[2]);



                   Configurationconf = new Configuration();
                   outputDir.getFileSystem(conf).delete(outputDir,true);
                   // 表示job 名称,可以自定义,一般是类名
                   StringjobName = SortApp2.class.getSimpleName();
                   // 把所有的相关内容都封装到job
                   Jobjob = Job.getInstance(conf, jobName);
                   // 打成jar 运行必备代码
                   job.setJarByClass(SortApp2.class);

                   // 设置输入路径
                   FileInputFormat.setInputPaths(job,inputPath);
                   // 设置输出目录
                   FileOutputFormat.setOutputPath(job,outputDir);

                   // 设置自定义mapper
                   job.setMapperClass(SortMapper.class);
                   // 指定k2,v2 类型
                   job.setMapOutputKeyClass(TwoInt.class);
                   job.setMapOutputValueClass(NullWritable.class);

                   // 自定义比较类
                   job.setSortComparatorClass(CustomSortComparator.class);
                   job.setNumReduceTasks(numReduceTasks);

                   // 设置自定义reduce
                   job.setReducerClass(SortReducer.class);
                   // 指定k3 v3 类型
                   job.setOutputKeyClass(TwoInt.class);
                   job.setOutputValueClass(NullWritable.class);

                   // 提交给yarn 运行,等待结束
                   job.waitForCompletion(true);
         }


         publicstatic class SortMapper extends Mapper{
                   TwoIntk2 = new TwoInt();
                   @Override
                   protectedvoid map(LongWritable key, Text value,
                                     Mapper.Context context)
                                                        throwsIOException, InterruptedException {
                            Stringline = value.toString();
                            String[]splited = line.split("\t");

                            k2.set(Integer.parseInt(splited[0]),Integer.parseInt(splited[1]));
                            context.write(k2,NullWritable.get());
                   }
         }

         publicstatic class SortReducer extends Reducer{
                   @Override
                   protectedvoid reduce(TwoInt k2, Iterable v2s,
                                     Reducer.Context context)
                                                        throwsIOException, InterruptedException {
                            context.write(k2,NullWritable.get());
                   }
         }

         publicstatic class CustomSortComparator extends WritableComparator{

                   /**
                    * 必须有无参构造方法,在方法内部,调用父类的含有2 个形参的构造方法。
                    * 父类构造方法的第二个参数为true
                    */
                   publicCustomSortComparator() {
                            super(TwoInt.class,true);
                   }

                   @Override
                   publicint compare(WritableComparable a, WritableComparable b) {
                            TwoIntaa = (TwoInt) a;
                            TwoIntbb = (TwoInt) b;
                            intret1 = aa.getFirst().compareTo(bb.getFirst());
                            if(ret1==0){
                                     returnaa.getSecond().compareTo(bb.getSecond());
                            }else{
                                     returnret1;
                            }
                   }
         }

         publicstatic class TwoInt implements WritableComparable{
                   privateInteger first;
                   privateInteger second;

                   publicTwoInt() {
                            super();
                   }

                   publicvoid set(Integer first, Integer second) {
                            this.first= first;
                            this.second= second;
                   }

                   publicvoid write(DataOutput out) throws IOException {
                            out.writeInt(this.first);
                            out.writeInt(this.second);
                   }

                   publicvoid readFields(DataInput in) throws IOException {
                            this.first=in.readInt();
                            this.second= in.readInt();
                   }

                   publicint compareTo(TwoInt o) {
                            return0;
                   }

                   publicInteger getFirst() {
                            returnfirst;
                   }

                   publicvoid setFirst(Integer first) {
                            this.first= first;
                   }

                   publicInteger getSecond() {
                            returnsecond;
                   }

                   publicvoid setSecond(Integer second) {
                            this.second= second;
                   }

                   @Override
                   publicint hashCode() {
                            finalint prime = 31;
                            intresult = 1;
                            result= prime * result + ((first == null) ? 0 : first.hashCode());
                            result= prime * result + ((second == null) ? 0 : second.hashCode());
                            returnresult;
                   }

                   @Override
                   publicboolean equals(Object obj) {
                            if(this == obj)
                                     returntrue;
                            if(obj == null)
                                     returnfalse;
                            if(getClass() != obj.getClass())
                                     returnfalse;
                            TwoIntother = (TwoInt) obj;
                            if(first == null) {
                                     if(other.first != null)
                                               returnfalse;
                            }else if (!first.equals(other.first))
                                     returnfalse;
                            if(second == null) {
                                     if(other.second != null)
                                               returnfalse;
                            }else if (!second.equals(other.second))
                                     returnfalse;
                            returntrue;
                   }

                   @Override
                   publicString toString() {
                            returnfirst + "\t" + second;
                   }

         }

}

MapReduce代码实现1_第3张图片
运行结果:
MapReduce代码实现1_第4张图片



补充:在map 和reduce之间有个shuffle过程,可以简单的理解shuffle是将map的输出传到reduce中去。核心思想是:map中有个内存缓存区,存储着mapd的输出,存满了就写到文件中,所以的map都记录完了,就把产生的所有文件合并到一个文件中。reduce通过http得到map输出文件。

你可能感兴趣的:(java,大数据)