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