Hadoop之MapReduce運行理解__hadoop

來源:互聯網
上載者:User
package com.felix;import java.io.IOException;import java.util.Iterator;import java.util.StringTokenizer;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.IntWritable;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;/** *  * 描述:WordCount explains by Felix * @author Hadoop Dev Group */public class WordCount{    /**     * MapReduceBase類:實現了Mapper和Reducer介面的基類(其中的方法只是實現介面,而未作任何事情)     * Mapper介面:     * WritableComparable介面:實現WritableComparable的類可以相互比較。所有被用作key的類應該實現此介面。     * Reporter 則可用於報告整個應用的運行進度,本例中未使用。      *      */    public static class Map extends MapReduceBase implements            Mapper<LongWritable, Text, Text, IntWritable>    {        /**         * LongWritable, IntWritable, Text 均是 Hadoop 中實現的用於封裝 Java 資料類型的類,這些類實現了WritableComparable介面,         * 都能夠被序列化從而便於在分布式環境中進行資料交換,你可以將它們分別視為long,int,String 的替代品。         */        private final static IntWritable one = new IntWritable(1);        private Text word = new Text();                /**         * Mapper介面中的map方法:         * void map(K1 key, V1 value, OutputCollector<K2,V2> output, Reporter reporter)         * 映射一個單個的輸入k/v對到一個中間的k/v對         * 輸出對不需要和輸入對是相同的類型,輸入對可以映射到0個或多個輸出對。         * OutputCollector介面:收集Mapper和Reducer輸出的<k,v>對。         * OutputCollector介面的collect(k, v)方法:增加一個(k,v)對到output         */        public void map(LongWritable key, Text value,                OutputCollector<Text, IntWritable> output, Reporter reporter)                throws IOException        {            String line = value.toString();            StringTokenizer tokenizer = new StringTokenizer(line);            while (tokenizer.hasMoreTokens())            {                word.set(tokenizer.nextToken());                output.collect(word, one);            }        }    }    public static class Reduce extends MapReduceBase implements            Reducer<Text, IntWritable, Text, IntWritable>    {        public void reduce(Text key, Iterator<IntWritable> values,                OutputCollector<Text, IntWritable> output, Reporter reporter)                throws IOException        {            int sum = 0;            while (values.hasNext())            {                sum += values.next().get();            }            output.collect(key, new IntWritable(sum));        }    }    public static void main(String[] args) throws Exception    {        /**         * JobConf:map/reduce的job配置類,向hadoop架構描述map-reduce執行的工作         * 構造方法:JobConf()、JobConf(Class exampleClass)、JobConf(Configuration conf)等         */        JobConf conf = new JobConf(WordCount.class);        conf.setJobName("wordcount");           //設定一個使用者定義的job名稱        conf.setOutputKeyClass(Text.class);    //為job的輸出資料設定Key類        conf.setOutputValueClass(IntWritable.class);   //為job輸出設定value類        conf.setMapperClass(Map.class);         //為job設定Mapper類        conf.setCombinerClass(Reduce.class);      //為job設定Combiner類        conf.setReducerClass(Reduce.class);        //為job設定Reduce類        conf.setInputFormat(TextInputFormat.class);    //為map-reduce任務設定InputFormat實作類別        conf.setOutputFormat(TextOutputFormat.class);  //為map-reduce任務設定OutputFormat實作類別        /**         * InputFormat描述map-reduce中對job的輸入定義         * setInputPaths():為map-reduce job設定路徑數組作為輸入列表         * setInputPath():為map-reduce job設定路徑數組作為輸出資料行表         */        FileInputFormat.setInputPaths(conf, new Path(args[0]));        FileOutputFormat.setOutputPath(conf, new Path(args[1]));        JobClient.runJob(conf);         //運行一個job    }}

(1)Map-Reduce的處理過程主要涉及以下四個部分: 用戶端Client:用於提交Map-reduce任務job JobTracker:協調整個job的運行,其為一個Java進程,其main class為JobTracker TaskTracker:運行此job的task,處理input split,其為一個Java進程,其main class為TaskTracker HDFS:hadoopDistributed File System,用於在各個進程間共用Job相關的檔案

(2)負責控制及調度MapReduce的job是JobTracker,負責運行MapReduce的job是TaskTracker。

(3)當client執行runJob(),向Hadoop提出請求處理一個job,Hadoop將job分成兩種task進行處理。即:map task和reduce task。

(4)Hadoop將輸入資料分成固定大小塊(這個快不能大於HDFS中每個block的大小64M)存放於DataNode上,這個過程我們稱之為input split。注意:輸入時Text的話則是按照行來split。Hadoop為每一個input split建立一個task,在此task中依次處理split中的一個個記錄(record)。

(5)Hadoop盡量讓輸入資料區塊所在的DataNode和task所執行的DataNode(每個DataNode上都有一個taskTracker)為同一個,可以提高運行效率,所以input split的大小也是HDFS中block的大小。

(6)Map task的輸出一般都是Reduce task的輸入,Reduce的輸出是整個job的輸出,往往會儲存在HDFS中。

(7)Reduce task中相同的key的所有記錄一定會到同一個task Tracker中運行。然而,不同的key可以在不同的task Tracker上運行,我們稱之為partition。

        Partition規則:(K2,V2)->Integer,也即,根據k2,產生一個partition的id,具有相同id的k2則進入同一個partition,被同一個TaskTracker上被同一個Reducer進行處理。

(8)任務提交

JobClient.runJob()建立一個新的JobClient執行個體,調用其submitJob()函數。 向JobTracker請求一個新的job ID 檢測此job的output配置 計算此job的input splits 將Job運行所需的資源拷貝到JobTracker的檔案系統中的檔案夾中,包括job jar檔案,job.xml設定檔,input splits 通知JobTracker此Job已經可以運行了

提交任務後,runJob每隔一秒鐘輪詢一次job的進度,將進度返回到命令列,直到任務運行完畢。

  (9)任務初始化

 

當JobTracker收到submitJob調用的時候,將此任務放到一個隊列中,job調度器將從隊列中擷取任務並初始化任務。

初始化首先建立一個對象來封裝job啟動並執行tasks, status以及progress。

在建立task之前,job調度器首先從共用檔案系統中獲得JobClient計算出的input splits。

其為每個input split建立一個map task。

每個task被分配一個ID。

  (10)任務分配

 

TaskTracker周期性的向JobTracker發送heartbeat。

在heartbeat中,TaskTracker告知JobTracker其已經準備運行一個新的task,JobTracker將分配給其一個task。

在JobTracker為TaskTracker選擇一個task之前,JobTracker必須首先按照優先順序選擇一個Job,在最高優先順序的Job中選擇一個task。

TaskTracker有固定數量的位置來運行map task或者reduce task。

預設的調度器對待map task優先於reduce task

當選擇reduce task的時候,JobTracker並不在多個task之間進行選擇,而是直接取下一個,因為reduce task沒有資料本地化的概念。

  (11)任務執行

 

TaskTracker被分配了一個task,下面便要運行此task。

首先,TaskTracker將此job的jar從共用檔案系統中拷貝到TaskTracker的檔案系統中。

TaskTracker從distributed cache中將job運行所需要的檔案拷貝到本地磁碟。

其次,其為每個task建立一個本地的工作目錄,將jar解壓縮到檔案目錄中。

其三,其建立一個TaskRunner來運行task。

TaskRunner建立一個新的JVM來運行task。

被建立的child JVM和TaskTracker通訊來報告運行進度。   3.4.1、Map的過程

MapRunnable從input split中讀取一個個的record,然後依次調用Mapper的map函數,將結果輸出。

map的輸出並不是直接寫入硬碟,而是將其寫入緩衝memory buffer。

當buffer中資料的到達一定的大小,一個背景線程將資料開始寫入硬碟。

在寫入硬碟之前,記憶體中的資料通過partitioner分成多個partition。

在同一個partition中,背景線程會將資料按照key在記憶體中排序。

每次從記憶體向硬碟flush資料,都產生一個新的spill檔案。

當此task結束之前,所有的spill檔案被合并為一個整的被partition的而且排好序的檔案。

reducer可以通過http協議請求map的輸出檔案,tracker.http.threads可以設定http服務線程數。 3.4.2、Reduce的過程

當map task結束後,其通知TaskTracker,TaskTracker通知JobTracker。

對於一個job,JobTracker知道TaskTracer和map輸出的對應關係。

reducer中一個線程周期性的向JobTracker請求map輸出的位置,直到其取得了所有的map輸出。

reduce task需要其對應的partition的所有的map輸出。

reduce task中的copy過程即當每個map task結束的時候就開始拷貝輸出,因為不同的map task完成時間不同。

reduce task中有多個copy線程,可以並行拷貝map輸出。

當很多map輸出拷貝到reduce task後,一個背景線程將其合并為一個大的排好序的檔案。

當所有的map輸出都拷貝到reduce task後,進入sort過程,將所有的map輸出合并為大的排好序的檔案。

最後進入reduce過程,調用reducer的reduce函數,處理排好序的輸出的每個key,最後的結果寫入HDFS。

 

  3.5、任務結束

 

當JobTracker獲得最後一個task的運行成功的報告後,將job得狀態改為成功。

當JobClient從JobTracker輪詢的時候,發現此job已經成功結束,則向使用者列印訊息,從runJob函數中返回。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.