第九天 - MapReduce计算模型 - 案例

第九天 - MapReduce计算模型 - 案例

一、概念

  • MapReduce是一种编程模型,用于大规模数据集的并行运算。能自动完成计算任务的并行化处理,自动划分计算数据和计算任务,在集群节点上自动分配和执行任务以及收集计算结果。
  • 在函数式语言里,map表示对一个列表中的每个元素做计算,reduce表示对一个列表中的每个元素做迭代计算。
  • 在MapReduce里,Map处理的是原始数据,可能是杂乱无章的,Reduce处理的是分组后的数据。

二、流程

  1. MapReduce结构
    • MRAppMaster:负责整个程序的过程调度及状态协调
    • MapTask:负责Map阶段的整个数据处理流程
    • ReduceTask:负责Reduce阶段的整个数据处理流程
  2. 运行流程

    • 最先启动的是MRAppMaster,MRAppMaster启动后根据本次job的描述信息,计算出需要的MapTask实例数量,然后向集群申请机器启动相应数量的MapTask进程
    • MapTask进程启动之后,根据给定的数据切片范围进行数据处理
    • MRAppMaster监控到所有MapTask进程任务完成之后,会根据客户指定的参数启动相应数量的ReduceTask进程,并告知ReduceTask进程要处理的数据范围(数据分区)
    • ReduceTask进程启动之后,根据MRAppMaster告知的待处理数据所在位置,从若干台MapTask运行所在机器上获取到若干个MapTask输出结果文件,并在本地进行重新归并排序,然后按照相同key的KV为一个组,调用用户定义的reduce()方法进行逻辑运算,并收集运算输出的结果KV,然后调用用户指定的outputformat将结果数据输出到外部存储

    三、案例一 - WordCount

准备工作
  1. 新建java项目,导包

    导包是在第七天-编写代码前的准备的基础上再导入以下内容:

    $HADOOP_HOME\share\hadoop\mapreduce下非test和非source的jar包

    $HADOOP_HOME\share\hadoop\mapreduce\lib下非test和非source的jar包

    $HADOOP_HOME\share\hadoop\yarn下非test和非source的jar包

    $HADOOP_HOME\share\hadoop\yarn\lib下非test和非source的jar包

    导入的包与之前的包会重复,覆盖即可

    导包后将lib添加至构建路径。

  2. 在src下建包org.apache.hadoop.io.nativeio,下载NativeIO.java将其复制到org.apache.hadoop.io.nativeio

  3. 将Hadoop配置文件中的log4j.properties复制到工程src目录下

  4. 建立三个包mapper、reducer、master对应放Mapper、Reducer、Master代码程序

  5. 最终包结构:

    1536215348145

编写代码
  1. WordCountMapper.java:

    在mapper包中新建此类,并继承Mapper类

    import java.io.IOException;
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.LongWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Mapper;
    //KEYIN -> 代表数据偏移量,传入的是数据类型
    //VALUE -> 每一行的数据,Text类型
    //KEYOUT -> 每个拆分得到的单词,作为Map阶段输出的key类型
    //VALUEOUT -> 单词出现次数:1,作为Map阶段输出的value类型
    public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
            throws IOException, InterruptedException {
        // TODO 自动生成的方法存根
        String line = value.toString();
        String[] words = line.split(" ");
        for (String word : words) {
            context.write(new Text(word), new IntWritable(1));
        }
    }
    }
  2. WordCountReducer.java:

    在reducer包中建立此类,并继承Reducer类

    import java.io.IOException;
    
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Reducer;
    // 前两个为Reducer阶段输入类型,与Mapper中的输出类型相一致
    public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    
    @Override
    protected void reduce(Text key, Iterable<IntWritable> value,
            Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
        // TODO 自动生成的方法存根
        int count = 0;
        // 迭代器获取值
        for(IntWritable intWritable : value) {
            count += intWritable.get();
        }
        context.write(new Text(key), new IntWritable(count));
    }
    
    }
  3. WordCountMaster.java:

    在master包中建立此类,此类作为程序入口,有main方法

    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Job;
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    
    import com.qf.mr.mapper.WordCountMapper;
    import com.qf.mr.reducer.WordCountReducer;
    
    public class WordCountMaster {
    public static void main(String[] args) throws Exception {
        // 初始化配置
        Configuration conf = new Configuration();
        // 初始化job参数,指定job名称
        Job job = Job.getInstance(conf, "wordCount");
        // 设置运行job的类
        job.setJarByClass(WordCountMaster.class);
        // 设置Mapper类
        job.setMapperClass(WordCountMapper.class);
        // 设置Reducer类
        job.setReducerClass(WordCountReducer.class);
        // 设置Map的输出数据类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        // 设置Reducer的输出数据类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        // 设置输入的路径,此路径必须存在,否则报错,此路径中存放数据文件,无论多少个文件,都会进行        // 数据计算
        FileInputFormat.setInputPaths(job, new Path("hdfs://SZ01:8020/input/user1"));
        // 设置输出的路径,程序运行后会自动创建
        FileOutputFormat.setOutputPath(job, new Path("hdfs://SZ01:8020/output/wordCount"));
        // 提交job
        boolean result = job.waitForCompletion(true);
        // 执行成功后进行后续操作
        if (result) {
            System.out.println("Congratulations!");
        }
    }
    }
运行程序

两种运行方式:

  • 直接在eclipse中运行WordCountMaster.java

    通过查看运行日志,可以得出,每运行一个job都会分配一个唯一的job_id,在运行过程控制台会显示运行过程:map xx% reduce xx% 显示程序运行的完成度。

    1536216018616

    程序运行结束后,控制台输出如下信息:

    1536216084228

    1536216108828

    运行成功后,可在hdfs的/output/wordCount中查看到结果

    hdfs dfs -ls /output/wordCount

    1536216315387

    _SUCCESS代表成功,输出结果在part-r-00000中

    hdfs dfs -cat /output/wordCount/part-r-00000

    1536216403292

  • 在CentOS中运行

    1. 由于输出结果的目录不能重复,所以改动Master中的输出结果目录

      1536216552578

    2. 将项目打包成jar:

      右键项目 -> export -> JAR file -> 选中src下的全部内容,去掉lib文件夹 -> 选择导出目录

      1536216675362

    3. 使用Xftp将jar文件上传至CentOS中

    4. 输入命令运行

      hadoop jar test.jar com.qf.mr.master.WordCountMaster

      hadoop jar {jar包所在位置} {运行主类的全路径}

    5. 运行状态

      1536216847714

    6. 可以通过yarn的http端口进行查看任务状态

      1536216930502

      在此页面,可以点击ID,在新页面中点击logs可以查看运行日志

      1536218379210

      可以在hdfs中查看结果文件

      1536218468492

四、WordCount优化

第三中的WordCount中输入输出路径固定了,可以将其作为可变参数,在运行时进行传参

  1. 修改WordCountMaster代码

    // 设置输入的路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        // 设置输出的路径
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

    1536218814877

  2. 打包后通过Xftp传入到CentOS中

  3. 执行运行命令时加上输入、输出路径参数

    hadoop jar test.jar com.qf.mr.master.WordCountMaster /input/user1 /output/wordCount2

    1536218927950

  4. 运行成功,查看结果

    1536218977316

    1536219001657

五、案例二 - 计算每一行中多个数值的平均值

数据文件

Line1 8 5 8
Line2 10 66 32 54
Line3 25 30 35 40 45

AvgMaster.java

import java.io.IOException;

import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

// 输入数据如下:
// 8 5 8
// 10 66 32 54
// 25 30 35 40 45
public class AvgMapper extends Mapper<LongWritable, Text, Text, FloatWritable> {

    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, FloatWritable>.Context context)
            throws IOException, InterruptedException {
        // 获得每一行的值
        String line = value.toString();
        // 分割数据,获得每一个数字的值
        String[] words = line.split(" ");
        String lineName = words[0];
        float sum = 0;
        float avg = 0;
        for(int i = 1; i < words.length; i++) {
            sum += Integer.parseInt(words[i]);
        }
        avg = sum / (words.length - 1);
        context.write(new Text(lineName), new FloatWritable(avg));
    }
}

AvgReducer.java

import java.io.IOException;

import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class AvgReducer extends Reducer<Text, FloatWritable, Text, FloatWritable> {

    @Override
    protected void reduce(Text key, Iterable<FloatWritable> values,
            Reducer<Text, FloatWritable, Text, FloatWritable>.Context context) throws IOException, InterruptedException {
            // 迭代器获取值
        context.write(key, new FloatWritable(values.iterator().next().get()));
    }

}

AvgMaster.java

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.FloatWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import com.qf.mr.mapper.AvgMapper;
import com.qf.mr.reducer.AvgReducer;

public class AvgMaster {
    public static void main(String[] args) throws Exception{
        // 初始化配置
        Configuration conf = new Configuration();
        // 初始化job参数,指定job名称
        Job job = Job.getInstance(conf, "avg");
        // Job job = Job.getInstance(conf, "wordCount");
        // 设置运行job的类
        job.setJarByClass(AvgMaster.class);
        // 设置Mapper类
        job.setMapperClass(AvgMapper.class);
        // 设置Reducer类
        job.setReducerClass(AvgReducer.class);
        // 设置Map的输出数据类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FloatWritable.class);
        // 设置Reducer的输出数据类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FloatWritable.class);
        // 设置输入的路径
        FileInputFormat.setInputPaths(job, new Path("hdfs://SZ01:8020/input/avgData"));
        // 设置输出的路径
        FileOutputFormat.setOutputPath(job, new Path("hdfs://SZ01:8020/output/avgOut"));
        // 提交job
        boolean result = job.waitForCompletion(true);
        // 执行成功后进行后续操作
        if (result) {
            System.out.println("Congratulations!");
        }
    }
}

执行成功,结果如下:

1536222352050

猜你喜欢

转载自blog.csdn.net/cry970795248/article/details/82463691