MapReduce里 实现多个job任务 包含(迭代式、依赖式、链式):

 

一、迭代式,所谓的迭代式,下一个执行的Job任务以上一个Job的输出作为输入,最终得到想要的结果。


1. Job job = new Job(new Configuration(),“test”);  
2.   
3. JobConf jobConf=(JobConf) job.getConfiguration();  
4. jobConf.setJobName("hadoopJoinTask");  
5.   
6. //设置job输入路径
7. FileInputFormat.setInputPaths(inputPath);  
8. //设置job输出的路径
9. FileOutputFormat.setOutputPath(jobConf, outPath);  
10.   
11. Job job2 = new Job(new Configuration(),“test2”);  
12.   
13. JobConf jobConf2=(JobConf) job2.getConfiguration();  
14. jobConf2.setJobName("hadoopJoinTask");  
15.   
16. //设置job2输入路径  job的输出路径
17. FileInputFormat.setInputPaths(outPath);  
18. //设置job2输出的路径
19. FileOutputFormat.setOutputPath(jobConf2, outPath2);
二、依赖式,工作中经常遇到这样的情况,比如job3需要等job1、job2、、、等执行完才能执行,因此job3是依赖于其他的job完成才能执行。
(推荐使用JobControl来进行多job控制,使用submit提交多job任务到集群执行时,在waitfor阶段容易出现丢失连接的情况,无法从集群获知作业执行情况)

1. //hadoop2  查看hadoop源码 JobControl 发现有ControlledJob,  ControlledJob里有依赖方法  addDependingJob
2.   
3.         Job job = new Job(new Configuration(),"job1");  
4.         Job job2 = new Job(new Configuration(),"job2");  
5.   
6.         ControlledJob controlledJob=new ControlledJob(job.getConfiguration());  
7.   
8. //设置job
9.         controlledJob.setJob(job);  
10.   
11.         ControlledJob controlledJob2=new ControlledJob(job2.getConfiguration());  
12.         controlledJob2.setJob(job2);  
13. //这里就是设置job依赖的重要方法了,依赖于<span style="font-size: 9pt; font-family: Menlo;">controlledJob</span><span style="font-size: 9pt; font-family: Menlo;">  </span>
14.         controlledJob.addDependingJob(controlledJob);  
15.   
16.         JobControl jc=new JobControl("jc");  
17.         jc.addJob(controlledJob);  
18.         jc.addJob(controlledJob2);  
19. //由于JobControl实现了Runnable 接口,而Runnable接口只有运行方法,没有结束方法,因此需要一个线程来辅助
20.   
21.         Thread jcThread = new Thread(jc);  
22.         jcThread.start();  
23.         while(true){  
24. //当job池里所有的job完成后,执行 下一步操作
25.             if(jc.allFinished()){  
26.                 System.out.println(jc.getSuccessfulJobList());  
27.                 jc.stop();  
28.   
29.   
30.             }  
31. //获取执行失败的job列表
32.             if(jc.getFailedJobList().size() > 0){  
33.                 System.out.println(jc.getFailedJobList());  
34.                 jc.stop();  
35.   
36.             }  
37.         }

三、链式



1. Configuration conf = new Configuration();  
2. Job job = new Job(conf);  
3. job.setJobName("ChianJOb");  
4. // 在ChainMapper里面添加Map1
5. Configuration map1conf = new Configuration(false);  
6. ChainMapper.addMapper(job, Map1.class, LongWritable.class, Text.class,  
7.         Text.class, Text.class, true, map1conf);  
8. // 在ChainReduce中加入Reducer,Map2;
9. Configuration reduceConf = new Configuration(false);  
10. ChainReducer.setReducer(job, Reduce.class, LongWritable.class,  
11.         Text.class, Text.class, Text.class, true, map1conf);  
12. Configuration map2Conf = new Configuration();  
13. ChainReducer.addMapper(job, Map2.class, LongWritable.class, Text.class,  
14.         Text.class, Text.class, true, <span style="font-family: Menlo; font-size: 9pt;">map2Conf</span><span style="font-size: 9pt; font-family: Menlo;">);</span>
在MapReduce计算框架中实现全局变量:
由于不同的task任务共享的只是jar文件的初始版本,且每个task在获取到yarn分配的资源后(container形式)分别运行在独立的JVM中,所以不同的task对Mapper(Reducer)实现类中成员变量以及主类中的成员变量的修改相互没有影响。


 


设置全局变量主要有三种方法:


 


一、配置Job属性


mapreduce的执行过程中task可以读取Job的属性。基于这个特性,程序可以在Job的配置代码中即main函数中利用Congfiguraion类的set函数将一些简单的数据结构封装到作业配置中;task任务启动的过程中(比如setup函数)通过Configuration类的get函数读取即可。


这种方法的优点是简单,资源消耗小;缺点则是只能共享一些小型的数据量,对大型的数据结构比较乏力。


下面的代码是在作业配置代码段中进行的全局变量的配置:


    1. int nAge = 25;
    2. Configuration conf = new Configuration();  
    3. conf.set("nAge", nAge);//主要是这一行
    4. Job job = new Job(conf, "JobName");
     
    1. protected void setup(Context context) throws IOException, InterruptedException {
    
    2. try {
    
    3.      Configuration conf = context.getConfiguration();
    4.  int nAge = conf.get("nAge");
    5. } catch (Exception e) {                
    6.         e.printStackTrace();
    7.     }  
    8. }


     


    二、使用DistributedCache分布式缓存


    在job驱动设置的时候添加:


    //分布式缓存要存储的文件路径
    String cachePath[] = {
                    "hdfs://10.105.32.57:8020/user/ad-data/tag/tag-set.csv",
                    "hdfs://10.105.32.57:8020/user/ad-data/tag/TagedUrl.csv"
            };
    //向分布式缓存中添加文件
            job.addCacheFile(new Path(cachePath[0]).toUri());
            job.addCacheFile(new Path(cachePath[1]).toUri());


    然后可以在Map,Reduce的setup阶段将缓存的文件读取出来,保存在内存当中。


    /*
     * 重写Mapper的setup方法,获取分布式缓存中的文件
     */
        @Override
        protected void setup(Mapper<LongWritable, Text, Text, Text>.Context context)
                       throws IOException, InterruptedException {
            // TODO Auto-generated method stub
            super.setup(context);
            URI[] cacheFile = context.getCacheFiles();
            Path tagSetPath = new Path(cacheFile[0]);
            Path tagedUrlPath = new Path(cacheFile[1]);
            文件操作(如把内容读到set或map中);
        }
    
    @Override
    public void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
                在map()中使用读取出的数据;
          }


     


    三、利用指定的HDFS文件进行全局变量的存储


    这种方法主要使用了java的API,预先定义数据的存储规则,通过读写HDFS中指定的文件按照预先定义的规则访问数据即可实现全局访问。HDFS中Java的API将在后面的文章中介绍。因为将数据存储在文件中,理论上可以存储足够所有应用可能的全局变量且能读能写还比较直观,缺点是需要使用IO,会占用系统资源,增加作业完成的资源消耗。