非常用心的好上手coding的MapReduce編程模型

一、前言

可以看這幾個視頻,挺棒的,可以快速上手MR開發。
先看這個鏈接的視頻,非常短,會大題有一個瞭解:
1、深入淺出講解MapReduece
再看這兩個,以經典的wordcount爲例,講解原理與編程:
2、wordcount程序原理以及代碼實現
3、mapreduce編程模型和具體實現框架之間的概念關係
有時間還可以看看3所在這個視頻集合裏的其他案例。

如果嫌視頻慢的話,那就看我下面的代碼以及文字吧。


我先以wordcount爲例,來學習。據前輩說,好多的業務邏輯本質上都是wordcount的邏輯。
可以大概看看下面的第二部分,直接看代碼,我在代碼裏面加了很多註釋,對整個邏輯寫的非常詳細啦。

二、wordcount原理及分析

1、如何統計單詞數?

在這裏插入圖片描述

2、MapReduce數據處理邏輯

在這裏插入圖片描述
要實現一個業務邏輯,關鍵在於:看我map這邊產生寫什麼key,value。相同key的會通過reduce把對應的那些value聚合起來。

3、MapReduce架構(可略過)

在這裏插入圖片描述
我早年博客,可以加深理解:
https://blog.csdn.net/u013317445/article/details/51769986

在寫代碼之前,一定要把wordcount的業務邏輯搞清楚。知道mapper的輸入輸出是啥,知道reduce的輸入輸出是啥。知道map()實現啥,reduce()實現啥

三、wordcount code

code主要分爲三部分:
Mapper部分:核心是重寫裏面的map()方法,把map階段的業務邏輯寫到map()方法裏面。
Reducer部分:核心是重寫裏面的reduce()方法,把reduce階段的業務邏輯寫到reduce()方法裏面。
Driver部分:在此封裝我們的MR程序相關運行參數。整個程序需要一個Drvier來進行提交。

WordcountMapper.java

package wc;/**
 * Created by cc on 2019/11/26.
 */

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;


import java.io.IOException;

/**
 * @program: WordC
 * @description: map
 * @author: chen
 * @create: 2019-11-26 16:41
 *
 * Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> 輸出輸入類型:
 * KEYIN:默認下,是mr框架所讀到的一行文本的起始偏移量,Long。
 *          但在hadoop裏面有更加精簡的接口,不直接用Long,而用LongWritable
 * VALUEIN:默認下,是mr框架所讀到的一行文本內容,String
 *          同上,用Text(org.apache.hadoop.io裏面的)
 * KEYOUT:是用戶自定義邏輯處理完成後輸出數據中的key,在此處是單詞,String。同上,用Text
 * VALUEOUT:用戶自定義邏輯完成之後輸出數據中的value,在此處是單詞詞頻,Integer
 *          同上,用IntWritable
 **/
public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {

    /**
     * 把map階段的業務邏輯寫到map()方法裏面
     * map task會對每一行輸入數據調用一次我們自定義的map()方法。原來是每一行數據調一次map!!!
     * 之後,每調一次map()會有一個輸出(一行數據處理完了有一個輸出)。map task會把這些輸出收集收集到一起。然後根據單詞的不同給不同的區域,再發給不同的reduce task
     * 實際上,有多個map tasks,每個去處理一部分文本
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //super.map(key, value, context);
        /**
         * map task會對每一行輸入數據調用一次map()
         * 那麼map()內要對這一行讀入的文本作何處理呢?
         * 1、將文本內容轉爲字符串
         * 2、按空格分割字符串,獲得每一個單詞
         * 3、每一個單詞,構建一個<word,1>,輸出<word,1>,寫到context裏
         **/
        String line= value.toString();
        String[] words= line.split(" ");
        for(String word:words){
            //context.write(word,1); 錯誤,Mapper的輸出類型是Text,IntWritable
            context.write(new Text(word), new IntWritable(1));

        }
    }
}

WordcountReducer.java

package wc;/**
 * Created by cc on 2019/11/27.
 */

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;


/**
 * @program: WordC
 * @description: reduce
 * @author: chen
 * @create: 2019-11-27 10:33
 *
 * Reducer<KEYIN, VALUEIN ,KEYOUT, VALUEOUT >輸入輸出類型:
 * KEYIN, VALUEIN對應Mapper輸出類型Text,IntWritable
 * KEYOUT, VALUEOUT是自定義Reducer的輸出數據類型
 * KEYOUT是單詞,類型Text
 * VALUEOUT是詞頻,類型IntWritable
 **/
public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
    /**
     * 把reduce階段的業務邏輯寫到reduce()方法裏面
     * reduce task拿到一組一組的數據,對每一組數據調一次reduce():
     * 如對<sun,1><sun,1><sun,1><sun,1><sun,1><sun,1>處理一次,調一次reduce();
     * 再下一個對<moon,1><moon,1><moon,1><moon,1>處理一次,調一次reduce(),......。
     *
     * reduce task數量可以指定,默認是1個
     *
     * 具體每一次reduce():
     * 對數據塊,key爲同一個,對應的value們串成了一個迭代器
     * 對value疊加,出count
     * 輸出<key,count>到context,整合出了次數
     * */
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        //super.reduce(key, values, context);
        int count=0;

        for(IntWritable value: values){
            count += value.get();//.get()轉爲int
        }
//        Iterator<IntWritable> it= values.iterator();
//        while(it.hasNext()){
//            count += it.next().get();
//        }

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

WordcountDriver.java

剛開始學,Driver這個就當成一個固定模板照着寫吧,只需要改一下參數和路徑。
主要在上面Mapper裏面的map()方法重寫,Reduecer裏面的reduce()方法重寫,把咱們的業務邏輯算法邏輯梳理清,就可以用代碼表達啦。

package wc;/**
 * Created by cc on 2019/11/27.
 */

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;


/**
 * @program: WordC
 * @description: 寫main()
 * @author: chen
 * @create: 2019-11-27 12:04
 *
 * 相當於yarn集羣的客戶端
 * 需要在此封裝我們的MR程序相關運行參數,這頂jar包
 * 最後提交給yarn
 **/

/**
 * map:本質是拆解,汽車拆成零件
 * reduce:本質是組合,汽車零件+其它各種機械零件組合成變形金剛
 * input(取數據)+split(切分)+map()+ shuffle(歸類數據)+reduce(組裝)+finalize(交付)
 * */

public class WordcountDriver {

    public static void main(String[] args) throws Exception {
        Configuration conf= new Configuration();
        Job job= Job.getInstance(conf);  //創建一個job

        //指定本程序的jar包所在的本地路徑(告訴yarn jar包在哪裏)
        //job.setJar("path");//寫死了路徑
        job.setJarByClass(WordcountDriver.class);//不寫死,就在jar包所在路徑


        //指定本業務job要使用的mapper/reduce業務類
        job.setMapperClass(WordcountMapper.class);
        job.setReducerClass(WordcountReducer.class);

        //指定mapper輸出數據類型,kv的
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //指定最終輸出數據類型,kv的
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
		
		//如果以後我們要自定義輸入輸出格式的話需要加上:
		//job.setInputFormatClass(MyInputFormat.class);
        //job.setOutputFormatClass(MyOutputFormat.class);

        //指定job的輸入原始文件目錄
        FileInputFormat.setInputPaths(job, new Path("F:\\ideaWorkspace\\WordC\\input"));//我把路徑寫死了
        //指定job的輸出結果所在目錄
        FileOutputFormat.setOutputPath(job, new Path("F:\\ideaWorkspace\\WordC\\output2"));

        //提交:將job中配置的相關參數,以及job所用的java類所在的jar包,提交給yarn去運行
        //job.submit();
        boolean res= job.waitForCompletion(true);
        System.exit(res? 0:1);//退出個0:job跑成功了
    }
}

四、梳理一下MR編程規範

  • 1、用戶編寫的程序分爲三個部分:Mapper、Reducer、Driver(提交運行mr程序的客戶端)
  • 2、Mapper的輸入數據是KV對的形式(KV類型可自定義)
  • 3、Reducer的輸出數據是KV對的形式(KV類型可自定義)
  • 4、Mapper中的業務邏輯寫在map()方法中
  • 5、map()方法(maptask進程)對每一個<K,V>調用一次。(處理一次<K,V>調一次map)
  • 6、Reducer的輸入數據類型對應Mapper的輸出數據類型,也是KV
  • 7、Reducer的業務邏輯寫在reduce()方法中
  • 8、Reduce()方法對每一組相同K的<K,V>組調用一次
  • 9、用戶的Mapper和Reducer都要繼承各自的類
  • 10、整個程序需要一個Drvier來進行提交,提交的是一個描述了各種必要信息的job對象

map task們全部輸出完了,reduce task們纔開始去map的機器上去拿數據。
map task們輸出數據時候,有outputCollector在收集<k1,v>,<k1,v><k1,v><k1,v>…
reduce task拿到一組一組的數據(相同key的爲一組)
map task可能和reduce task在不同的機器上。

五、補充一下hadoop mapreduce內置數據類型

在考慮KV的輸入輸出類型的時候需要到這個知識點。

  • BooleanWritable 標準布爾型數值
  • ByteWritable 單字節數值
  • DoubleWritable 雙字節數
  • FloatWritable 浮點數
  • IntWritable 整型數
  • LongWritable 長整型數
  • Text 使用UTF-8格式存儲的文本
  • NullWritable 當<key, value>中的key或value爲空時使用

這些內置數據類型,都實現了WritableComparable接口,以便用這些類型定義的數據可以被序列化進行網絡傳輸和文件存儲,以及進行大小比較。
我現在是能用內置類型就用內置類型,不用自己寫了。
我們也可以自定義數據類型,不過要自己寫一些接口。具體的話暫時沒用到。

六、進一步,寫Partitioner,寫Combiner

https://www.cnblogs.com/frankdeng/p/9256254.html 可以看一下這個博客後面的部分。寫了Partitioner把單詞按照ASCII碼奇偶分區,寫了Combiner對每一個maptask的輸出局部彙總。


好啦,就分享到這裏吧。the end~

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