MyJobLink鏈接MapReduce作業案例,新舊API比較

hadoop基礎部分的學習告一段落,休息了幾天,現在滿血復活了。。。哈哈,讓我們一起來學習學習hadoop的第一個鏈接MapReduce作業的案例吧。

在高階數據處理中,會經常發現無法將整個流程寫在單個MapReduce作業中,Hadoop支持將多個MapReduce程序鏈接成更大的作業。

1、順序鏈接MapReduce作業

雖然兩個作業可以手動的逐個執行,但更爲快捷的方式是生成一個自動化的執行序列,可以將MapReduce作業按照作業順序鏈接在一起,用一個MapReduce作業的輸出作爲下一個的輸入。MapReduce作業鏈接類似與Unix管道

mapreduce-1 | mapreduce-2 | mapreduce-3 | ...

每個作業的driver都必須創建一個新的job,並將當前輸入路徑設爲前一個的輸出。在最後階段刪除在鏈上每個階段生成的中間數據

2、複雜依賴的MapReduce鏈接

在複雜數據處理任務中的子任務並不是按順序運行的,則它們的MapReduce作業不能按線性方式鏈接。

若mapreduce-1處理一個數據集,MapReduce-2處理另一個數據集,而MapReduce-3對前兩個做內部連結,則MapReduce-3依賴於其他兩個作業,僅當MapReduce-1和MapReduce-2都完成後纔可以執行,而MapReduce-1和MapReduce-2之間並無相互依賴

hadoop的簡化機制,通過Job和JobControl類管理非線性作業間的依賴。Job對象是MapReduce作業的表現形式,Job對象的實例化可通過傳遞一個JobConf對象到作業的構造函數來實現,除了要保持作業的配置信息外,job還通過設定addDependingJob()方法維護作業的依賴關係。如x.addDependingJob(y)意味着x在y完成前不會啓動

3、預處理和後處理階段的鏈接

大量的數據處理任務涉及對記錄的預處理和後處理。可以爲預處理與後處理步驟各自編寫一個MapReduce作業,並把他們鏈接起來,由於過程中每一個步驟的中間結果都需要佔用I/O和存儲資源,這種做法是低效的,也可以自己寫mapper去預先調用所有的預處理步驟,在讓reducer調用所有的後處理步驟,這將強制你採用模塊化和可組合的方式來構建預處理和後處理。Hadoop在版本0.19.0中引入了ChainMapper和ChainReducer類來簡化預處理和後處理的構成。

可以通過僞正則表達式將MapReduce作業的鏈接符合化的表達爲:

[MAP | REDUCE] +

使用ChainMapper和ChainReduce所生成的作業表達式與此類似:

MAP+ | REDUCE | MAP+

作業按序執行多個mapper來預處理數據,並在運行reduce之後可選的按序執行多個mapper來做數據的後處理。

有這樣一個例子:有4個mappper(Map1,Map2,Map3,Map4)和一個reducer(Reduce),它們被鏈接爲單個MapReduce作業。
順序以下: Map1 | Map2 | Reduce | Map3 | Map4

在這個組合中,可以把Map2和Reduce視爲MapReduce作業核心,在Mapper和Reducer之間使用標準的分區和洗牌。把Map1作爲前處理步驟,Map3, Map4作爲後處理步驟

在driver中使用ChainMapper和ChainReducer類來設定這個mapper類和reducer類序列的構成。
ChainMapper使用模式:(預處理作業)
ChainReducer使用模式:(設置Reducer並添加後處理Mapper)


代碼清單:MapReduce作業鏈接(舊版本API)

package com.yc.demo;

import java.io.IOException;
import java.util.Iterator;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;
import org.apache.hadoop.mapred.lib.ChainMapper;
import org.apache.hadoop.mapred.lib.ChainReducer;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class MyJObLink  extends Configured implements Tool {

    public static class Reduce extends MapReduceBase implements Reducer<LongWritable,Text,Text,Text>{

        @Override
        public void reduce(LongWritable key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter)
                throws IOException {
            output.collect(new Text("1"), new Text("1"));
        }
        
    }
    
    public static class Map1 extends MapReduceBase implements Mapper<LongWritable,Text,Text,Text> {

        @Override
        public void map(LongWritable key, Text value, OutputCollector<Text, Text> output, Reporter reporter)
                throws IOException {
        output.collect(value, new Text(key.toString()));//V1(記錄)作鍵K2,K1(偏移量)作值V2
        }
        
    }
    
    public static class Map2 extends MapReduceBase implements Mapper<Text,Text,LongWritable,Text>{

        @Override
        public void map(Text key, Text value, OutputCollector<LongWritable, Text> output, Reporter reporter)
                throws IOException {
            output.collect(new LongWritable(Long.valueOf(value.toString())),key);//輸入鍵值對,交換後作鍵值對輸出
        }
    }
    
    public static class Map3 extends MapReduceBase implements Mapper<Text,Text,LongWritable,Text> {

        @Override
        public void map(Text key, Text value, OutputCollector<LongWritable, Text> output, Reporter reporter)
                throws IOException {
            output.collect(new LongWritable(Long.valueOf("1")),key);//輸入鍵值對後輸出鍵爲1,值爲輸入鍵
        }
        
    }
    
    public static class Map4 extends MapReduceBase implements Mapper<LongWritable,Text,LongWritable,Text>{

        @Override
        public void map(LongWritable key, Text value, OutputCollector<LongWritable, Text> output, Reporter reporter)
                throws IOException {
            output.collect(new LongWritable(Long.valueOf("1")), new Text("1"));//輸入鍵值對後輸出鍵爲1,值爲1
        }
    }
    
    @Override
    public int run(String[] args) throws Exception {
        //1、實例化作業對象
        Configuration conf = getConf();
        JobConf job = new JobConf(conf);
        
        job.setJobName("xiaoxiaoChainJob");
        //2、爲作業設置輸入文本格式化和輸出文本的格式化
        job.setInputFormat(TextInputFormat.class);
        job.setOutputFormat(TextOutputFormat.class);
        
        //3、爲作業設置輸入文件和輸出文件的路徑
        Path in = new Path(args[0]);
        Path out = new Path(args[1]);
        FileInputFormat.setInputPaths(job, in);
        FileOutputFormat.setOutputPath(job, out);
        
        /**
         * 在作業添加Map1階段
         * 使用ChainMapper.addMapper()添加Reduce之前的所有步驟
         *
         * ChainMapper.addMapper(JobConf job,Class<? extends Mapper<LongWritable,Text,Text,Text>> klass,
         *           Class<? extends LongWritable> inputKeyClass,Class<? extends Text> outputKeyClass,Class<? extends Text> outputValueClass,
         *           boolean byValue,JobConf mapperConf)
         *           該方法有8個參數,第一個和最後一個分別爲全局和本地JobConf對象,第二個參數(klass)是Mapper類,負責數據處理
         *           餘下4個參數inputKeyClass,inputValueClass,outputKeyClass,outputValueClass是這個Mapper類中輸入/輸出類的類型
         *           byValue這個參數,在標準的Mapper模型中,鍵/值對的輸出在序列化之後寫入磁盤(鍵和值實現爲Writable使得它們能夠被複制和序列化),
         *           等待被洗牌到一個可能完全不同的節點上,形式上認爲這個過程採用的是值傳遞(passed by value),發送的是鍵值對的副本
         *           在目前的情況下我們可以將一個Mapper與另一個相鏈接,在相同的JVM線程中一起執行,因此,鍵/值對的發送有可能採用引用傳遞(passed by reference)
         *           初始Mapper的輸出放到內存中,後續的Mapper直接引用相同的內存位置
         *           當Mapper 1調用OutputCollector.collect<K k,V v)時,對象k和v直接傳遞給Map2的map()方法,
         *           mapper之間可能有大量的數據需要傳遞,避免去複製這些數據可以讓性能得以提高.
         *  但是,這樣會違背Hadoop中MapReduceApi的一個更爲微妙的"約定",即對OutputCollector.collect(K k,V v)
         *  的調用一定不會改變k和v的內容.
         *  Map1調用OutputCollector.collect(K k,V v)之後,可以繼續使用對象k和v,並完全相信他們的值會保持不變.
         *  但如果我們將這些對象通過引用傳遞給Map2,接下來Map2可能會改變他們,這就違反了API的"約定".
         *  如果你確信Map1的map()方法在調用OutputCollector.collect(K k,V v)之後不再使用k和v的內容,
         *  或者Map2並不改變k和v的在其上的輸入值,你可以通過設定byValue爲false來獲得一定的性能提升.
         *  如果你對Mapper的內部代碼不太瞭解,安全起見最好設byValue爲true,依舊採用值傳遞模式,
         *  確保mapper會按預期的方式工作.
         */
        //4、爲作業設置Mapper和Reducer函數
        //(1)在作業中添加Map1階段,使用ChainMapper.addMapper()添加位於Reduce之前的步驟
        JobConf map1Conf = new JobConf(false);
        ChainMapper.addMapper(job, Map1.class,LongWritable.class,Text.class,Text.class,Text.class, true,map1Conf);
        
        /**
         * 在作業中添加Map2階段
         * 使用ChainMapper.addMapper()添加位於Reduce之前的所有步驟
         */
        JobConf map2Conf=new JobConf(false);
        ChainMapper.addMapper(job, Map2.class, Text.class, Text.class, LongWritable.class, Text.class, true, map2Conf);
        /**
         * 在作業中添加Reduce階段
         * 使用靜態的ChainReducer.setReducer()方法設置reducer
         */
        JobConf reducerConf = new JobConf(false);
        ChainReducer.setReducer(job, Reduce.class, LongWritable.class, Text.class, Text.class, Text.class, true, reducerConf);
        /**
         * 在作業中添加Map3階段
         * 使用ChainReducer.addMapper()添加reducer後續的步驟
         */
        JobConf map3Conf=new JobConf(false);
        ChainReducer.addMapper(job, Map3.class, Text.class, Text.class, LongWritable.class, Text.class, true, map3Conf);
        /**
         * 在作業中添加Map4階段
         * 使用ChainReducer.addMapper()添加reducer後續的步驟
         */
        JobConf map4Conf = new JobConf(false);
        ChainReducer.addMapper(job, Map4.class,LongWritable.class,Text.class,LongWritable.class,Text.class,true,map4Conf);
        
        //啓動作業
        JobClient.runJob(job);
        
        return 0;
    }

    public static void main(String [] args) throws Exception {
        /*final String inputPath = "/home/dev/hadooptest/mapin/cite";
        final String outputPath="/home/dev/hadooptest/mapin/cite/out";
        String [] paths = {inputPath,outputPath};*/
        
        /**
         *  Driver中的main函數->ToolRunner中的run函數->Too接口中的run函數->
         *  Driver中覆蓋函數處理參數->Driver中核心函數啓動job(合併爲一個方法,重寫了接口Tool的run方法)
         *  args(運行時動態給定的)、paths(代碼中定死的)爲輸入文本和輸出文本的路徑,
         */
        int res=ToolRunner.run(new Configuration(), new MyJObLink(),args);
        /*int res=ToolRunner.run(new Configuration(), new MyJObLink(),paths);*/
        System.exit(res);
    }
}

MapReduce作業鏈接(新版本API

package com.yc.link;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.conf.Configured;

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.chain.ChainMapper;

import org.apache.hadoop.mapreduce.lib.chain.ChainReducer;

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

import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;

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

import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

import org.apache.hadoop.util.Tool;

import org.apache.hadoop.util.ToolRunner;

 

public class MyJobLink extends Configured implements Tool{

      /**

      * Reducer任務 Reduce

      * @author 王雲飛

      *

      */

      public static class Reduce extends Reducer<LongWritable,Text,Text,Text>{

             @Override

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

                    context.write(new Text("1"), new Text("1"));

             }

      }

      /**

      * Mapper任務 Map1

      * @author 王雲飛

      *

      */

      public static class Map1 extends Mapper<LongWritable,Text,Text,Text>{

             @Override

             public void map(LongWritable key, Text value, Context context)

                           throws IOException, InterruptedException {

                    context.write(value, new Text(key.toString()));// V1(記錄)作鍵K2,K1(偏移量)作值V2

             }      

      }

      /**

      * Mapper任務 Map2

      * @author hadoop

      *

      */

      public static class Map2 extends Mapper<Text,Text,LongWritable,Text>{

             @Override

             public void map(Text key, Text value, Context context)

                           throws IOException, InterruptedException {

                    context.write(new LongWritable(Long.valueOf(value.toString())), key);// 輸入鍵值對交換後作鍵值對輸出

            

             }

      }

      /**

      * Mapper任務 Map3

      * @author 王雲飛

      *

      */

      public static class Map3 extends Mapper<Text,Text,LongWritable,Text>{

             @Override

             public  void map(Text key, Text value, Context context)

                           throws IOException, InterruptedException {

                    context.write(new LongWritable(Long.valueOf("1")), key);// 輸入鍵值對後輸出鍵爲1,值爲輸入鍵

            

             }

      }

      /**

      * Mapper任務 Map4

      * @author 王雲飛

      *

      */

      public static class Map4 extends Mapper<LongWritable,Text,LongWritable,Text>{

             @Override

             public  void map(LongWritable key, Text value, Context context)

                           throws IOException, InterruptedException {

                    context.write(new LongWritable(Long.valueOf("1")), new Text("1"));// 輸入鍵值對後輸出鍵爲1,值爲1

             }

      }

      /**

      * driver類

      */

      @Override

      public int run(String[] args) throws Exception {

             // 1.實例化作業對象

             Configuration conf=this.getConf();

             Job job=new Job(conf,"飛哥ChainJob");

             job.setJarByClass(MyJobLink.class);

            

             // 2.爲作業設置輸入文件和輸出文件的路徑

             FileInputFormat.addInputPath(job, new Path(args[0]));

             FileOutputFormat.setOutputPath(job, new Path(args[1]));

            

             //3.爲作業設置輸入文本的格式化和輸出文本的格式化

             job.setInputFormatClass(TextInputFormat.class);

             job.setOutputFormatClass(TextOutputFormat.class);

            

             // 4.爲作業設置Mapper 和Reducer函數

             //(1)在作業中添加Map1階段, 使用ChainMapper.addMapper()添加位於Reduce之前的步驟

             //(job, klass, inputKeyClass, inputValueClass, outputKeyClass, outputValueClass, mapperConf

             Configuration map1Conf=new Configuration(false);

             ChainMapper.addMapper( job,

                                                                                                Map1.class,

                                                                                                LongWritable.class,

                                                                                                Text.class,

                                                                                                Text.class,

                                                                                                Text.class,

                                                                                                map1Conf);

             // (2)在作業中添加Map2階段, 使用ChainMapper.addMapper()添加位於Reduce之前的步驟

             Configuration map2Conf=new Configuration(false);

             ChainMapper.addMapper( job,

                                                                                                Map2.class,

                                                                                                Text.class,

                                                                                                Text.class,

                                                                                                LongWritable.class,

                                                                                                Text.class,

                                                                                                map2Conf);

             //(3)在作業中添加Reduce階段,使用ChainReducer.setReducer()方法設置Reducer

             Configuration reduceConf=new Configuration(false);

             //job, klass, inputKeyClass, inputValueClass, outputKeyClass, outputValueClass, reducerConf

             ChainReducer.setReducer(job,

                                                                                           Reduce.class,

                                                                                           LongWritable.class,

                                                                                           Text.class,

                                                                                           Text.class,

                                                                                           Text.class,

                                                                                           reduceConf);

             //(4)在作業中添加Map3階段,使用ChainReducer.addMapper()添加reducer後續的步驟

             Configuration map3Conf=new Configuration(false);

             ChainReducer.addMapper( job,

                                                                                                Map3.class,

                                                                                                Text.class,

                                                                                                Text.class,

                                                                                                LongWritable.class,

                                                                                                Text.class,

                                                                                                map3Conf);

            

             // (5)在作業中添加Map4階段,使用ChainReducer.addMapper()添加reducer後續的步驟

             Configuration map4Conf=new Configuration(false);

             ChainReducer.addMapper( job,

                                                                                                Map4.class,

                                                                                                LongWritable.class,

                                                                                                Text.class,

                                                                                                LongWritable.class,

                                                                                                Text.class,

                                                                                                map4Conf);   

             // 5.啓動作業

             return (job.waitForCompletion(true)?0:1);

      }

      /**

      * 主函數

      * @param args

      * @throws Exception

      */

      public static void main(String [] args) throws Exception{

             int res=ToolRunner.run(new Configuration(), new MyJobLink(), args);

             System.exit(res);

      }

}

和小夥伴一人寫了一個版本的,但輸出結果是一樣的,輸出結果如下:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章