Hadoop基础(八) --- 分布式计算MapReduce案例,Windows 测试MR作业的异常解决,MR作业的流程分析

一、分布式计算MapReduce
--------------------------------------------------------
    1.目的:取出历年来的最高气温(使用1901和1902数据源)

    2.Map阶段
        map(k,v)函数: 对输入数据的每一行进行map处理,需要自己编程实现,k--行号,v--数据
        
    3.Reduce
        reduce()函数:三个阶段
        a.Shuffle --> 从每个map节点复制map输出数据,将数据进行重洗,然后分配
        b.Sort --> 框架按key对input数据排序和整合(对相同的key的value进行合并),形成新的key values(合并后的values)
        c.Reduce--> 对于key 和 values 进行目标提取,然后写出到outPath
    
    4.MapReduce的作业流程:
        map --> shuffle --> reduce
        
        a.编写自定义map类 : MyMapper,重写map方法
        
        b.编写自定义Reduce类: MyReducer
                    
        c.编写app程序,运行mr作业: MaxTemperature
        
            
二、Job
--------------------------------------------
    1.允许用户配置作业,提交作业,查询状态,控制执行。
    2.set 方法设置属性,只能在提交作业之前进行。
    3.用户创建job之后,可以查看job的各方面信息
    4.用户可以监控job过程
    

三、Windows 测试Job,异常解决
----------------------------------------------
java.lang.NullPointerExceptionjava.lang.ProcessBuilder.start(UnknownSource)atorg.apache.hadoop.util.Shell.runCommand(Shell.java:482)Exceptioninthread"main"java.lang.UnsatisfiedLinkError:org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z
    解决方案:
        解决方法是下载https://github.com/srccodes/hadoop-common-2.2.0-bin文件然后将其中的hadoop.dll文件放到hadoop安装路径的bin文件夹下(配置好HADOOP_HOME的环境变量),然后重启电脑,这样问题就能得到解决了!
    如果还是不行,请检查本地的java安装目录是否有空格,比如progeam files ...,如果有,请尝试将jdk安装到无空格目录或者采用下面的方法
        找到windows hadoop安装目录,E:\hadoop-2.7.3\etc\hadoop\hadoop-env.cmd,修改其中的JAVA_HOME(目录是C:\Program Files\Java\jdk1.8.0_121,因为Program Files中存在空格,所以出现错误)只需要用
    PROGRA~1代替Program Files即可
        
        
四、MR作业的流程分析
---------------------------------------------------------------------
    AppMain() //程序main主函数入口
        -->job.waitForCompletion(true)  //等待直到MR作业完成
            ---> job.submit()        //job,提交作业
                --> (JobSubmitter)submitter.submitJobInternal(Job.this, cluster)    //内部提交
                    --> (ClientProtocol)submitClient.submitJob()        //真正的提交
                        --> LocalJobRunner.submitJob()                    //本地作业提交
                            --> (LocalJobRunner.Job) new job()            //新建LocalJobRunner$Job对象
                            
                                --> LocalJobRunner.Job.run()                //new job() 过程中,开启线程run()
                                    
                                    --> SplitMetaInfoReader.readSplitMetaInfo()    //读取数据分割元数据等信息。进行任务分配
                                    --> 开始顺序执行任务,首先是map任务,然后是reduce任务
                                    --> runTasks(mapRunnables, mapService, "map");    //开始运行任务集合
                                        --> runTasks(reduceRunnables, reduceService, "map");
                                        --> 循环执行 service.submit(r)    //提交分任务(Map任务)-- 之前有几个spilt,这里就有几个maptask
                                        --> 每循环一次,就会开启一个分线程
                                            -->LocalJobRunner.Job.MapTaskRunnable.run()//service.submit(r)开启的线程的run()函数
                                                --> map.run()        //运行map任务。此时会开启分线程去运行map task
                                                    --> (MapTask) runNewMapper()                            
                                                        --> (MyMapper)mapper.run(mapperContext);    //此处开始执行自定义的MyMapper类的map方法,进行map过程
                                    
                                    --> 一直运行,直到map过程结束,紧接着开始执行reduce                                    
                                    --> runTasks(reduceRunnables, reduceService, "reduce");        //开始执行reduce任务
                                        --> 循环执行 service.submit(r);    //提交分任务(Reduce任务)
                                            --> reduce.run(localConf, Job.this);    //运行reduce任务
                                                --> shuffleConsumerPlugin = ReflectionUtils.newInstance()   //反射加载shuffle类
                                                -->    shuffleConsumerPlugin.run()                                //开始shuffle
                                                --> sortPhase.complete()                                    //排序结束
                                                --> runNewReducer();                                        //开始执行reduce任务
                                                --> reducer.run(reducerContext);
                                                --> MyReducer.reduce();                                        //此处开始执行自定义的reduce方法
                                
                                --> 至此LocalJobRunner.Job.run()线程运行结束
                --> (JobSubmitter)submitter.submitJobInternal(Job.this, cluster)    //运行结束
        --> waitForCompletion() 结束,返回0
    --> app结束,整个mr作业结束

五、案例代码部分
----------------------------------------------------------------------------

**************************************
1.App入口函数类
**************************************

public class MaxTemperature {

    private static String inPath = "file:///D:\\test\\in";
    private static String outPath = "file:///D:\\test\\out5";
    
    
    public static void main(String[] args) {
        
        try 
        {
            //开启一个MapReduce的JOB
            Job job = Job.getInstance();
            //设定app运行类(main类)
            job.setJarByClass(MaxTemperature.class);
            //设定job的名称
            job.setJobName("Max temperature");
            //设定数据输入路径
            FileInputFormat.addInputPath(job, new Path(inPath));
            //设定数据输出路径
            FileOutputFormat.setOutputPath(job, new Path(outPath));
            //设定MapClass
            job.setMapperClass(MyMapper.class);
            //设定ReduceClass
            job.setReducerClass(MyReducer.class);
            //设定输出key类型
            job.setOutputKeyClass(Text.class);
            //设定输出value类型
            job.setOutputValueClass(IntWritable.class);
            //等待job结束
            job.waitForCompletion(true);            
        } 
        catch (Exception e) 
        {    
            e.printStackTrace();
        } 
        
    }
    
}


**************************************
2.Map类
**************************************

/**
 * Map类
 * @author Administrator
 *
 */
public class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{

    private static final int MISSING = 9999;
    
    /**
     * Map方法,对数据进行map处理
     */
    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
            throws IOException, InterruptedException {
        
        //将Writable转化成普通java对象
        long index = key.get();                //获取输入的一行数据的索引
        String line = value.toString();        //获取输入的一样数据的内容
        
        //获取感兴趣的值:年份和温度
        String year = line.substring(15, 19);
        int airTemperature;
            
        if (line.charAt(87) == '+') 
        { 
            // parseInt doesn't like leading plus signs
            airTemperature = Integer.parseInt(line.substring(88, 92));
        } 
        else 
        {
            airTemperature = Integer.parseInt(line.substring(87, 92));
        }
        //空气质量
        String quality = line.substring(92, 93);
        
        //分析结果正确与否,并且输出年份和气温(输出到reduce中进行下一步计算)
        if (airTemperature != MISSING && quality.matches("[01459]")) {
            context.write(new Text(year), new IntWritable(airTemperature));
        }
    
    }
}


**************************************
3.Reduce类
**************************************

/**
 * Reduce类
 * @author Administrator
 *
 */
public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{

    
    @Override
    public void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException 
    {
        //获取气温中的最大值
        int maxValue = Integer.MIN_VALUE;
        for (IntWritable value : values) 
        {
            maxValue = Math.max(maxValue, value.get());
        }
        context.write(key, new IntWritable(maxValue));
    }
}


 

猜你喜欢

转载自blog.csdn.net/xcvbxv01/article/details/82054510