14mapreduce的流程說明&實現Mapper類&實現Driver以及測試運行——好程序

 

mapreduce核心名詞
job:用戶的一個計算請求稱爲一個作業。
task:每一個作業,都需要分拆成多個的task,交由多個主機來完成,拆分出來的執行單元就叫任務。
task又分爲3種類型:
map:負責map階段的整個的數據處理流程(需要資源)
reduce:負責reduce階段的整個數據處理流程(需要資源)
appMaster:負責整個程序的過程調度以及狀態協調(需要資源,ResourceManager首先分給它)

mapreduce的程序運行流程
1、一個mr程序啓動的時候,最先啓動的時mrAppMaster,mrAppmaster啓動後根據本次job的描述信息(job:split分片信息),計算出需要的maptask的實例數量,然後向集羣申請機器來啓動maptask任務。
2、maptask任務啓動之後,根據給定的數據切片範圍進行數據處理,主要流程:
    根據客戶端指定的inputformat來獲取recordreader來讀取數據,形成輸入kv對。
    將輸入的kv對傳遞給客戶自定義的map方法,做邏輯運算,並將map方法的輸出的kv對進行收集到緩存。
    
  將緩存中的kv對按照k進行分區排序後不斷的溢寫到磁盤文件(什麼樣的數據分到哪個reduce裏面)
   到此maptask任務已經完成了
3、mrappmaster監控所有的maptask任務完成情況,會根據客戶指定的參數啓動相應數量的reduce任務,把那個告知reducetask要處理的數據範圍(數據分區)(reduce任務是客戶端人爲指定的,數據分成多少個task,就會分成多少個分區)

4、reducetask任務啓動之後,根據mrappmaster告知的數據範圍所在的位置,從若干臺maptask運行的主機上獲取若干個maptask的輸出結果文件,並在本機進行重新歸併排序。然後按照相同key的kv分爲一組(有多少k就分多少組),調用客戶定義的reduce方法(業務邏輯),進行邏輯運算,並將運算結果輸出成kv,然後調用客戶指定的outputformat將結果輸出到外部存儲設備。

(map將資源分到磁盤的分區裏,然後reduce被告知所做的任務,及任務所在的磁盤文件位置信息,則就可以主動去拉去資源)

 

編寫一個mapreduce程序編程步驟(框架都是接口,只要將寫好的程序放入接口裏)
01、用戶編程的程序分成三個部分:Mapper、Reducer、Driver(提交mr程序的客戶端,Driver是驅動程序)
02、Mapper的輸入數據是KV對形式(KV的類型可以自定義)
03、Mapper的輸出數據是KV對形式(KV的類型可以自定義)
04、Mapper中的業務邏輯寫在map方法中(對一整行數據進行拆分,然後數組迭代,再進行一個單詞一個數量統計)
05、map方法(由maptask進程來調用)對每一個KV對調用一次map方法
06、reduce的輸入數據是KV對形式,數據類型對應的是map階段的輸出數據類型
07、reducer的業務邏輯寫在reduce方法中
08、reducetask進程對每一組相同key的kv鍵值對數據分成一組,調用一次reduce方法        到此,功能的角度已經完成了
09、用戶自定義的Mapper和Reducer類都要繼承各自的父類。          這裏是框架的角度來看
10、整個程序需要一個Driver來執行提交,提交的是一個描述了各種必要信息的job對象(job裏面要設置相應的屬性)

 

要有hadoop-client纔可以寫MapReduce程序

需求:下面數據有很多文件,將數據放到目錄裏面,將目錄傳給程序就OK了

hello world
hello qianfeng
hello gp1923
hello java
hello hadoop
hello cls
hello ma
hello hai

1233211234567

Mapper,Reduce,Driver的編寫 

/**
 * Copyright (C), 2015-2019, XXX有限公司
 * FileName: wordcount
 * Author:   Chenfg
 * Date:     2019/7/10 0010 15:20
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改時間           版本號              描述
 */
package qfedu.com.bigdata.mr;

import org.apache.hadoop.conf.Configuration;
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.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;

/**
 *
 * @author Chenfg
 * @create 2019/7/10 0010
 * @since 1.0.0
 */
public class wordcount {
    /**
     * Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
     *     框架(maptask)在調用咱們寫的map業務方法時,會將數據作爲參數(一個key,一個value)傳遞給map方法
     *     KEYIN:是框架要傳遞給咱們自定義的 map方法的輸入參數中的key的數據類型
     *     VALUEIN:是框架要傳遞給咱們自定義的map方法的輸入參數中的value的數據類型
     *
     *     在默認情況下,
     *     框架傳入的key是框架從待處理數據(文本文件)中讀取到的“某一行”的起始偏移量,類型是Long
     *     框架傳入的value是框架從待處理數據中讀取到的“某一行”的內容,所以類型是String
     *
     *     但是,Long或者String等java原生類型的序列化效率比較低下,所以hadoop對其進行了封裝,有替代品:
     *     LongWritable、Text
     *
     *     map方法在處理完成數據之後需要返回結果(一個key一個value)
     *     KEYOUT:是咱們的map方法處理完成後要返回結果中的key的類型
     *     VALUEOUT:是咱們的map方法處理完成後要返回結果中的value的類型
     *
     *      我們定義的map方法的調用規律:maptask每讀一行數據就調用一次map方法
     */
    static class WCMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
        private static Text k = new Text();
        private static IntWritable v = new IntWritable(1);
        /**
         * maptask會對每一對輸入數據調用一次map方法
         * @param key
         * @param value
         * @param context
         * @throws IOException
         * @throws InterruptedException
         */
        @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) {
                //將單詞作爲key,將次數1作爲value,輸出
                k.set(word);
                context.write(k,v);
            }
        }
    }

    /**
     *      Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
     *      reduce方法要接收的輸入參數是一個key和一個value<T>的迭代器
     *      KEYIN:是框架要傳遞給reduce方法的輸入參數中的 key的數據類型   --->對應map方法的輸出的key的類型
     *      VALUEIN:是框架要傳遞給reduce方法的輸入參數中的value迭代器中所要迭代的數據類型   --->對應map方法的輸出的value的數據類型
     *
     *      KEYOUT:是reduce方法處理完成後要輸出的數據中key的類型
     *      VALUEOUT:是reduce方法處理完成後要輸出的數據中value的類型
     *
     *      reduce方法的調用規律:框架會從map階段的輸出結果中挑選出所有key相同的kv對組成一組,調用一次reduce方法
     */
    static class WCReducer extends Reducer<Text, IntWritable, Text, LongWritable>{
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
            /**
             *cls,[1]
             * gp1923,[1]
             */
            long count=0;

            for (IntWritable value: values) {
                count+=value.get();
            }

            //輸出
            context.write(key,new LongWritable(count));
        }
    }

    /**
     * 相當於yarn的一個客戶端
     * 需要在此封裝我們的mr程序的相關運行參數,指定運行jar包
     * 最後提交job給yarn集羣
     * @param args
     */
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //該類是運行在hadoop客戶端,main一旦運行,yarn的客戶端就會啓動起來,與yarn的服務端進行通信
        //yarn的服務端就會啓動mapreduce程序並使用Mapper和Reducer類

        //設置運行參數
        Configuration conf = new Configuration();

//        conf.set("fs.defaultFS","hdfs://hadoop0001:9000");

        //創建job對象
        Job job = Job.getInstance(conf, "wordcount");

        //指定本業務job使用的本地路徑
        job.setJarByClass(wordcount.class);

        //指定本業務job要執行哪個Mapper類
        job.setMapperClass(WCMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //指定Reduce端要執行哪個reduce類
        job.setReducerClass(WCReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        //指定輸入輸出文件目錄
        FileInputFormat.setInputPaths(job,new Path(args[0]));

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

        //提交job
        boolean b = job.waitForCompletion(true);

        System.exit(b?0:1);

//        job.submit();
    }
}

 

map的輸出:
hello,14
world,1
hello,1
qianfeng,1
hello,1
gp1923,1
hello,1
java,1
hello,1
hadoop,1
hello,1
cls,1
hello,1
ma,1
hello,1
hai,1

排序之後的數據:
cls,1
gp1923,1
hadoop,[1]
hai,[1]
hello,[1,1,1,1,1,1,1,1]
java,1
ma,1
qianfeng,1
world,1

0分區
caijincun    1
gp1923    1
qianfeng    1
zhouzhiduo    1

1分區
laozhaotou    1

3分區
cls    [1,1]
hadoop    1
hello,1 hello,1 hello,1...    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
world    1
 

 

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