10小时入门大数据:第5章-分布式计算框架MapReduce

MapReduce概述

    源于google论文
    是google的克隆版
    优点:海量数据离线处理,易开发,易运行
    缺点:实时流式计算

从wordcount案例说起mapreduce编程模型

通过wordcount词频统计分析案例入门
    wordcount:统计文件中每个单词出现的次数
    需求:求wc
1)文件内容小:shell
2)文件内容很大: TB GB ??? 如何解决大数据量的统计分析
    场景如:>求 url TOPN < wc的延伸,工作中很多场景的开发都是wc的基础上进行改造的
    借助于分布式计算框架来解决了: mapreduce
    分而治之的思想,先分开后合并
    输入文件被拆分成多个块,一个块splitting交由一个mapping处理(通常情况下这个块和blocklsize对应),处理完成之后写到本地,通过shuffling 网络传输将相同的key写到一个reducing里边去,然后再reducing里边把结果统计起来,最终写到文件系统上边去

mapreduce执行流程

MapReduce编程模型之Map和Reduce阶段
    将作业拆分成Map阶段和Reduce阶段
    map阶段:Map Tasks
    reduce阶段:Reduce Tasks
执行步骤
    准备map处理输入数据
    mapper处理
    shuffle
    reduce处理,几个reduce输出就是几个文件
    结果输出
输入输出的过程
    (input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)
mapreduce核心概念
    Split:交由MapReduce作业来处理的数据块,是MapReduce中最小的计算单元
    HDFS:blocksize 是HDFS中最小的存储单元 128M
    默认情况下:他们两是一一对应的,当然我们也可以手工设置他们之间的关系(不建议)
    InputFormat:将我们的输入数据进行分片(split): InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
    TextInputFormat: 处理文本格式的数据
    OutputFormat: 输出
    Combiner
    Partitioner

mapreduce1.x架构

1)JobTracker: JT
    作业的管理者 管理的
    将作业分解成一堆的任务:Task(MapTask和ReduceTask)
    将任务分派给TaskTracker运行
    作业的监控、容错处理(task作业挂了,重启task的机制)
    在一定的时间间隔内,JT没有收到TT的心跳信息,TT可能是挂了,TT上运行的任务会被指派到其他TT上去执行
2)TaskTracker: TT
    任务的执行者 干活的
    在TT上执行我们的Task(MapTask和ReduceTask)
    会与JT进行交互:执行/启动/停止作业,发送心跳信息给JT
3)MapTask
    自己开发的map任务交由该Task出来
    解析每条记录的数据,交给自己的map方法处理
    将map的输出结果写到本地磁盘(有些作业只仅有map没有reduce==>HDFS)
4)ReduceTask
    将Map Task输出的数据进行读取
    按照数据进行分组传给我们自己编写的reduce方法处理
    输出结果写到HDFS
mapreduce2.x架构
    参考YARN 的架构解析

java版本wordcount功能实现

package com.imooc.hadoop.mapreduce;

import org.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;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

/**
 * 使用MapReduce开发WordCount应用程序
 */
public class WordCountApp {

    /**
     * Map:读取输入的文件
     */
    public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{

        LongWritable one = new LongWritable(1);

        @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) {
                // 通过上下文把map的处理结果输出
                context.write(new Text(word), one);
            }

        }
    }

    /**
     * Reduce:归并操作
     */
    public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {

        @Override
        protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {

            long sum = 0;
            for(LongWritable value : values) {
                // 求key出现的次数总和
                sum += value.get();
            }

            // 最终统计结果的输出
            context.write(key, new LongWritable(sum));
        }
    }

    /**
     * 定义Driver:封装了MapReduce作业的所有信息
     */
    public static void main(String[] args) throws Exception{

        //创建Configuration
        Configuration configuration = new Configuration();

        //创建Job
        Job job = Job.getInstance(configuration, "wordcount");

        //设置job的处理类
        job.setJarByClass(WordCountApp.class);

        //设置作业处理的输入路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));

        //设置map相关参数
        job.setMapperClass(MyMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);

        //设置reduce相关参数
        job.setReducerClass(MyReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        //设置作业处理的输出路径
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

    运行方式,使用IDEA+Maven开发wc:
    1)开发
    2)编译:mvn clean package -DskipTests
    3)上传到服务器:scp target/hadoop-train-1.0.jar hadoop@hadoop000:~/lib
    4)运行hadoop jar /home/hadoop/lib/hadoop-train-1.0.jar com.imooc.hadoop.mapreduce.WordCountApp hdfs://hadoop000:8020/hello.txt hdfs://hadoop000:8020/output/wc
    其中com.imooc.hadoop.mapreduce.WordCountApp表示指定的主类
    hdfs://hadoop000:8020/hello.txt表示的是输入文件的路径,也就是代码中的 new Path(args[0]) 
    hdfs://hadoop000:8020/output/wc表示的是输出结果的路径,也就是代码中的new Path(args[1])

java版本wordcount功能重构

相同的代码和脚本再次执行,会报错

security.UserGroupInformation:

PriviledgedActionException as:hadoop (auth:SIMPLE) cause:

org.apache.hadoop.mapred.FileAlreadyExistsException:

Output directory hdfs://hadoop000:8020/output/wc already exists

Exception in thread "main" org.apache.hadoop.mapred.FileAlreadyExistsException:

Output directory hdfs://hadoop000:8020/output/wc already exists

在MR中,输出文件是不能事先存在的
    1)先手工通过shell的方式将输出文件夹先删除hadoop fs -rm -r /output/wc,可以写shell脚本,先删除后执行
    2)在代码中完成自动删除功能: 推荐大家使用这种方式

Path outputPath = new Path(args[1]);
 	FileSystem fileSystem = FileSystem.get(configuration);
 	if(fileSystem.exists(outputPath)){
 	  fileSystem.delete(outputPath, true);
 	  System.out.println("output file exists, but is has deleted");
 	}

完整代码如下:

package com.wxx.hadoop.mapreduce;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

/**
 * 使用MapReduce开发WordCount应用程序
 */
public class WordCount2App {

    /**
     * Map:读取输入的文件
     */
    public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{

        LongWritable one = new LongWritable(1);

        @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) {
                // 通过上下文把map的处理结果输出
                context.write(new Text(word), one);
            }

        }
    }

    /**
     * Reduce:归并操作
     */
    public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {

        @Override
        protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {

            long sum = 0;
            for(LongWritable value : values) {
                // 求key出现的次数总和
                sum += value.get();
            }

            // 最终统计结果的输出
            context.write(key, new LongWritable(sum));
        }
    }

    /**
     * 定义Driver:封装了MapReduce作业的所有信息
     */
    public static void main(String[] args) throws Exception{

        //创建Configuration
        Configuration configuration = new Configuration();

        // 准备清理已存在的输出目录
        Path outputPath = new Path(args[1]);
        FileSystem fileSystem = FileSystem.get(configuration);
        if(fileSystem.exists(outputPath)){
            fileSystem.delete(outputPath, true);
            System.out.println("output file exists, but is has deleted");
        }

        //创建Job
        Job job = Job.getInstance(configuration, "wordcount");

        //设置job的处理类
        job.setJarByClass(WordCount2App.class);

        //设置作业处理的输入路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));

        //设置map相关参数
        job.setMapperClass(MyMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);

        //设置reduce相关参数
        job.setReducerClass(MyReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        //设置作业处理的输出路径
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

combiner应用程序开发

    本地的reducer
    减少map task输出的数据量和数据网络传输量
    增加Combiner设置后执行hadoop jar /home/hadoop/lib/hadoop-train-1.0.jar com.imooc.hadoop.mapreduce.CombinerApp hdfs://hadoop000:8020/hello.txt hdfs://hadoop000:8020/output/wc
    使用场景:求和、次数 +,对于求平均数是不可以的错的。

package com.wxx.hadoop.mapreduce;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

/**
 * 使用MapReduce开发WordCount应用程序
 */
public class CombinerApp {

    /**
     * Map:读取输入的文件
     */
    public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{

        LongWritable one = new LongWritable(1);

        @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) {
                // 通过上下文把map的处理结果输出
                context.write(new Text(word), one);
            }

        }
    }

    /**
     * Reduce:归并操作
     */
    public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {

        @Override
        protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {

            long sum = 0;
            for(LongWritable value : values) {
                // 求key出现的次数总和
                sum += value.get();
            }

            // 最终统计结果的输出
            context.write(key, new LongWritable(sum));
        }
    }

    /**
     * 定义Driver:封装了MapReduce作业的所有信息
     */
    public static void main(String[] args) throws Exception{

        //创建Configuration
        Configuration configuration = new Configuration();

        // 准备清理已存在的输出目录
        Path outputPath = new Path(args[1]);
        FileSystem fileSystem = FileSystem.get(configuration);
        if(fileSystem.exists(outputPath)){
            fileSystem.delete(outputPath, true);
            System.out.println("output file exists, but is has deleted");
        }

        //创建Job
        Job job = Job.getInstance(configuration, "wordcount");

        //设置job的处理类
        job.setJarByClass(CombinerApp.class);

        //设置作业处理的输入路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));

        //设置map相关参数
        job.setMapperClass(MyMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);

        //设置reduce相关参数
        job.setReducerClass(MyReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        //通过job设置combiner处理类,其实逻辑上和我们的reduce是一模一样的
        job.setCombinerClass(MyReducer.class);

        //设置作业处理的输出路径
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

partitioner应用程序开发

    partitioner决定map task输出的数据交由哪个reduce task处理
    默认实现:分发的key的hash值对reduce task个数取模
    开发实例 :统计不同品牌手机两天的销量
    执行hadoop jar /home/hadoop/lib/hadoop-train-1.0.jar com.imooc.hadoop.mapreduce.ParititonerApp hdfs://hadoop000:8020/partitioner hdfs://hadoop000:8020/output/partitioner

 	package com.wxx.hadoop.mapreduce;
 	
 	import org.apache.hadoop.conf.Configuration;
 	import org.apache.hadoop.fs.FileSystem;
 	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.Partitioner;
 	import org.apache.hadoop.mapreduce.Reducer;
 	import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 	import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 	
 	import java.io.IOException;
 	
 	public class ParititonerApp {
 	
 	    /**
 	     * Map:读取输入的文件
 	     */
 	    public static class MyMapper 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(" ");
 	
 	            context.write(new Text(words[0]), new LongWritable(Long.parseLong(words[1])));
 	
 	        }
 	    }
 	
 	    /**
 	     * Reduce:归并操作
 	     */
 	    public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
 	
 	        @Override
 	        protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
 	
 	            long sum = 0;
 	            for(LongWritable value : values) {
 	                // 求key出现的次数总和
 	                sum += value.get();
 	            }
 	
 	            // 最终统计结果的输出
 	            context.write(key, new LongWritable(sum));
 	        }
 	    }
 	
 	    public static class MyPartitioner extends Partitioner<Text, LongWritable> {
 	
 	        @Override
 	        public int getPartition(Text key, LongWritable value, int numPartitions) {
 	
 	            if(key.toString().equals("xiaomi")) {
 	                return 0;
 	            }
 	
 	            if(key.toString().equals("huawei")) {
 	                return 1;
 	            }
 	
 	            if(key.toString().equals("iphone7")) {
 	                return 2;
 	            }
 	
 	            return 3;
 	        }
 	    }
 	    /**
 	   * 定义Driver:封装了MapReduce作业的所有信息
 	   */
 	    public static void main(String[] args) throws Exception{
 	
 	        //创建Configuration
 	        Configuration configuration = new Configuration();
 	
 	        // 准备清理已存在的输出目录
 	        Path outputPath = new Path(args[1]);
 	        FileSystem fileSystem = FileSystem.get(configuration);
 	        if(fileSystem.exists(outputPath)){
 	            fileSystem.delete(outputPath, true);
 	            System.out.println("output file exists, but is has deleted");
 	        }
 	
 	        //创建Job
 	        Job job = Job.getInstance(configuration, "wordcount");
 	
 	        //设置job的处理类
 	        job.setJarByClass(ParititonerApp.class);
 	
 	        //设置作业处理的输入路径
 	        FileInputFormat.setInputPaths(job, new Path(args[0]));
 	
 	        //设置map相关参数
 	        job.setMapperClass(MyMapper.class);
 	        job.setMapOutputKeyClass(Text.class);
 	        job.setMapOutputValueClass(LongWritable.class);
 	
 	        //设置reduce相关参数
 	        job.setReducerClass(MyReducer.class);
 	        job.setOutputKeyClass(Text.class);
 	        job.setOutputValueClass(LongWritable.class);
 	
 	        //设置job的partition
 	        job.setPartitionerClass(MyPartitioner.class);
 	        //设置4个reducer,每个分区一个
 	        job.setNumReduceTasks(4);
 	
 	        //设置作业处理的输出路径
 	        FileOutputFormat.setOutputPath(job, new Path(args[1]));
 	
 	        System.exit(job.waitForCompletion(true) ? 0 : 1);
 	    }
 	} 

jobhistory使用

    记录已运行完的mapreduce信息到指定的HDFS目录下
    默认是没有开启的
    配置vim mapred-site.xml
 

 	<property>
 	    <name>mapreduce.jobhistory.address</name>
 	    <value>master:10020</value>
 	    <description>MapReduce JobHistory Server IPC host:port</description>
 	</property>
 	
 	<property>
 	    <name>mapreduce.jobhistory.webapp.address</name>
 	    <value>master:19888</value>
 	    <description>MapReduce JobHistory Server Web UI host:port</description>
 	</property>
 	
 	<property>
 	    <name>mapreduce.jobhistory.done-dir</name>
 	    <value>/history/done</value>
 	</property>
 	
 	<property>
 	    <name>mapreduce.jobhistory.intermediate-done-dir</name>
 	    <value>/history/done_intermediate</value>
 	</property>

配置yarn聚合参数vim yarn-site.xml
 

<property>
 	<name>yarn.log-aggregation-enable</name>
 	<value>true</value>
</property>

重启一下hadoop和yarn
启动history./mr-jobhistory-daemon.sh start historyserver

在浏览器中点击history就能看见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值