MapReduce开发流程及示例

文章目录

    • MapReduce开发流程
      • (1)输入数据接口:InputFormat
      • (2)逻辑处理接口:Mapper
      • (3)Partitioner分区
      • (4)Comparable排序
      • (5)Combiner合并
      • (6)逻辑处理接口:Reducer
      • (7)输出数据接口:OutputFormat

MapReduce开发流程

(1)输入数据接口:InputFormat

(1)默认使用的实现类是:TextInputFormat
(2)TextInputFormat的功能逻辑是:一次读一行文本,然后将该行的起始偏移量作为key,行内容作为value返回。
(3)CombineTextInputFormat可以把多个小文件合并成一个切片处理,提高处理效率。

(2)逻辑处理接口:Mapper

用户根据业务需求实现其中三个方法:map() setup() cleanup ()

hadoop中的MapReduce框架里已经预定义了相关的接口,其中如Mapper类下的方法setup()cleanup()setup(),此方法被MapReduce框架仅且执行一次,在执行Map任务前,进行相关变量或者资源的集中初始化工作。
若是将资源初始化工作放在方法map()中,导致Mapper任务在解析每一行输入时都会进行资源初始化工作,导致重
复,程序运行效率不高!
cleanup(),此方法被MapReduce框架仅且执行一次,在执行完毕Map任务后,进行相关变量或资源的释放工作。若是
将释放资源工作放入方法map()中,也会导致Mapper任务在解析、处理每一行文本后释放资源,而且在下一行文本解
析前还要重复初始化,导致反复重复,程序运行效率不高!

所以,建议资源初始化及释放工作,分别放入方法setup()cleanup()中进行
public static class WordMapper extends Mapper<LongWritable,Text,Text,LongWritable>{
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String line = value.toString();
            String[] words = line.split(",");
            for (String word : words) {
                context.write(new Text(word),new LongWritable(1));
            }
        }
    }
  • setup

  • 例1

private HashMap<String, String> stus = new HashMap<>();

//map task之前执行
//每次都会执行
@Override
protected void setup(Context context)throws I0Exception,InterruptedException {
	//获取文件系统的操作对象
	FileSystem fs= FileSystem. get( (context.getConfiguration());
	Path path = new Path( pathString:"/score.txt");
	FSDataInputStream open = fs.open(path);
	BufferedReader br = new BufferedReader (new InputStreamReader(open)) ;
	String line;
	while ((line=br. readLine()) !=null) { 
		stus.put(line.split(regex:",")[0],line);
		super.setup(context);
    }
}                       
  • 例2
private HashMap<String, String> pdMap = new HashMap<>();
    
    @Override
    protected void setup(Context context) throws IOException, InterruptedException {
        // 获取缓存的文件,并把文件内容封装到集合 pd.txt
        URI[] cacheFiles = context.getCacheFiles();
        FileSystem fs = FileSystem.get(context.getConfiguration());
        FSDataInputStream fis = fs.open(new Path(cacheFiles[0]));
        // 从流中读取数据
        BufferedReader reader = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
        String line;
        while (StringUtils.isNotEmpty(line = reader.readLine())) {
            // 切割
            String[] fields = line.split("\t");
            // 赋值
            pdMap.put(fields[0], fields[1]);
        }
        // 关流
        IOUtils.closeStream(reader);
    }

(3)Partitioner分区

(1)有默认实现 HashPartitioner,逻辑是根据key的哈希值和numReduces来返回一个分区号;

key.hashCode()&Integer.MAXVALUE % numReduces

(2)如果业务上有特别的需求,可以自定义分区。

  • FlowBean为自定义对象
  • 例1
//手机号分区
public class ProvincePartitioner2 extends Partitioner<FlowBean, Text> {
    @Override
    public int getPartition(FlowBean flowBean, Text text, int numPartitions) {

        String phone = text.toString();

        String prePhone = phone.substring(0, 3);

        int partition;
        if ("136".equals(prePhone)){
            partition =  0;
        }else if ("137".equals(prePhone)){
            partition =  1;
        }else if ("138".equals(prePhone)){
            partition =  2;
        }else if ("139".equals(prePhone)){
            partition =  3;
        }else {
            partition = 4;
        }

        return partition;
    }
}
  • 例2
//年龄分区
public class FlowCountPartition extends Partitioner<Text,FlowBean>{
    @Override
    public int getPartition(Text text, FlowBean flowBean, int i) {
        Integer age = Integer.parseInt(text.toString());
        // 年龄小于18 分到第 0 组
        if(age <= 18){
        	return 0;
        }
        // 年龄大于18 分到第一组
        if (age > 18){
        	return 1;
        }
	}
}

(4)Comparable排序

(1)当我们用自定义的对象作为key来输出时,就必须要实现WritableComparable接口,重写其中的compareTo()方法。
(2)部分排序:对最终输出的每一个文件进行内部排序。
(3)全排序:对所有数据进行排序,通常只有一个Reduce。
(4)二次排序:排序的条件有两个。

  • 二次排序
/**
 * 1、定义类实现writable接口
 * 2、重写序列化和反序列化方法
 * 3、重写空参构造
 * 4、toString方法
 */
public class FlowBean implements WritableComparable<FlowBean> {
    private long upFlow; // 上行流量
    private long downFlow; // 下行流量
    private long sumFlow; // 总流量

    // 空参构造
    public FlowBean() {
    }

    public long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(long upFlow) {
        this.upFlow = upFlow;
    }

    public long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(long downFlow) {
        this.downFlow = downFlow;
    }

    public long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    public void setSumFlow() {
        this.sumFlow = this.upFlow + this.downFlow;
    }

    @Override
    public void write(DataOutput out) throws IOException {

        out.writeLong(upFlow);
        out.writeLong(downFlow);
        out.writeLong(sumFlow);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.upFlow = in.readLong();
        this.downFlow = in.readLong();
        this.sumFlow = in.readLong();
    }

    @Override
    public String toString() {
        return upFlow + "\t" + downFlow + "\t" + sumFlow;
    }

    @Override
    public int compareTo(FlowBean o) {

        // 总流量的倒序排序
        if (this.sumFlow > o.sumFlow) {
            return -1;
        } else if (this.sumFlow < o.sumFlow) {
            return 1;
        } else {
            // 按照上行流量的正序排
            if (this.upFlow > o.upFlow) {
                return 1;
            } else if (this.upFlow < o.upFlow) {
                return -1;
            } else {
                return 0;
            }
        }
    }
}

(5)Combiner合并

Combiner合并可以提高程序执行效率,减少IO传输。但是使用时必须不能影响原有的业务处理结果。

  • 使用 CombineTextInputFormat
// 在job中 默认用的是TextInputFormat.class ,TextInputformat对任务的切片机制是按文件规划切片,不管文件多小,都会有一个单独的切片,都会交给一个maptask,如果有大量的小文件,就会产生大量的maptask,处理效率及其低下 
//CombineTextInputFormat用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个MapTask处理。
//设置CombineTextInputFormat
// setMaxInputSplitSize值为4M

job.setInputFormatClass(CombineTextInputFormat.class);

MapReduce开发流程及示例_第1张图片

  • 自编 Combine
//combine 预聚合 一个发生在reduce之前reduce端
public static class  CombineReducer extends Reducer<Text,LongWritable,Text,LongWritable>{
        @Override
        protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException,InterruptedException{
            long count=0;
            for (LongWritable value : values) {
                count+=value.get();
            }
            context.write(key,new LongWritable(count));
        }
    }

在job中
//combine 预聚合
job.setCombinerClass(CombineReducer.class);

(6)逻辑处理接口:Reducer

用户根据业务需求实现其中三个方法:reduce() setup() cleanup ()

public static class  GenderReducer extends Reducer<Text,LongWritable,Text,LongWritable>{
       @Override
       protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException{
            long count=0;
            for (LongWritable value : values) {
                count+=value.get();
            }
            context.write(key,new LongWritable(count));
        }
    }

(7)输出数据接口:OutputFormat

(1)默认实现类是TextOutputFormat,功能逻辑是:将每一个KV对,向目标文本文件输出一行。
(2)用户还可以自定义OutputFormat。

你可能感兴趣的:(hadoop,mapreduce,big,data,hadoop,大数据,分布式)