mapreduce(六):MapReduce原理
来源:互联网 发布:淘宝调研网站 编辑:程序博客网 时间:2024/05/20 18:47
MapReduce原理
执行步骤:
1、Map任务处理
1.1读取输入文件内容,解析成key、value对,对输入文件的每一行,解析成key、value对。每一个键值对调用一次Map函数。
1.2写自己的逻辑,对输入的key、value处理,转换成新的key、value输出。
1.3对输出的key、value进行分区。
1.4对不同分区的数据,按key进行排序、分组。相同key的value放到一个集合中。
1.5(可选)分组后的数据进行归约。
2、Reduce任务处理
2.1对多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点。
2.2对多个map任务的输出进行合并、排序。写reduce函数自己的逻辑,对输入的key、value处理,转换成新的key、value输出。
2.3把reduce的输出保存到文件中
MapReduce概述
* MapReduce是一种分布式计算模型,由google提出,主要用于搜索领域,解决海量数据的计算问题。
* MR由两个阶段组成:Map和Reduce,用户只需要实现map()和reduce()两个函数,即可实现分布式计算,非常简单。
* 这两个函数的形参是key、value对,表示函数的输入信息。
Hadoop1.0叫JobTracker 和 TaskTracker Hadoop2.0后叫ResourceManager 和 NodeManager
2.MR执行流程
(1).客户端提交一个mr的jar包给JobClient(提交方式:hadoop jar ...)
(2).JobClient通过RPC和JobTracker进行通信,返回一个存放jar包的地址(HDFS)和jobId
(3).client将jar包写入到HDFS当中(path = hdfs上的地址 + jobId)
(4).开始提交任务(任务的描述信息,不是jar, 包括jobid,jar存放的位置,配置信息等等)
(5).JobTracker进行初始化任务
(6).读取HDFS上的要处理的文件,开始计算输入分片,每一个分片对应一个MapperTask
(7).TaskTracker通过心跳机制领取任务(任务的描述信息)
(8).下载所需的jar,配置文件等
(9).TaskTracker启动一个java child子进程,用来执行具体的任务(MapperTask或ReducerTask)
(10).将结果写入到HDFS当中
序列化概念:
* 序列化(Serialization)是指把结构化对象转化为字节流。
* 反序列化(Deserialization)是序列化的逆过程。即把字节流转回结构化对象。
* Java序列化(java.io.Serializable)
Hadoop序列化的特点:
* 序列化格式特点:
1.紧凑:高效使用存储空间。
2.快速:读写数据的额外开销小。
3.可扩展:可透明地读取老格式的数据。
4.互操作性:支持多语言的交互。
Hadoop的序列化格式:Writable
案例1:计算一个文件中单词出现的次数(WorldCount)
1、linux上新建一个words文件,里面保存如下单词。
将该文件上传到hdfs上
2 在eclipse下新建以下三个类
WCMapper类
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
/**Mapper<KEYIN,VALUEIN,KEYOUT,VALUEOUT>**/
public class WCMapper extends Mapper<LongWritable,Text,Text,LongWritable> {
@Override
protected void map(LongWritable key, Text value,
Context context)
throws IOException, InterruptedException {
//接收数据V1
String line = value.toString();
//切分数据
String[] words = line.split(" ");
//循环
for(String w : words) {
context.write(new Text(w), new LongWritable(1));
}
}
}
WCReducer类
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class WCReducer extends Reducer<Text,LongWritable,Text,LongWritable>{
@Override
protected void reduce(Text key, Iterable<LongWritable> v2s,
Context context)
throws IOException, InterruptedException {
//接收数据
//定义一个计数器
long counter = 0;
//循环v2s
for(LongWritable i : v2s) {
counter += i.get();
}
//输出
context.write(key, new LongWritable(counter));
}
}
WorldCount类
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import org.apache.hadoop.conf.Configuration;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
/**
* 1.分析具有的业务逻辑,确定输出数据的样式
* 2.自定义一个类, 这个类要继承org.apache.hadoop.mapreduce.Mapper,
* 重写map方法,在map方法中实现具体业务逻辑,将新的key、value输出
* 3.自定义一个类,这个类要继承org.apache.hadoop.mapreduce.Reducer,
* 重写reduce方法,并实现具体业务逻辑,将新的key、value输出
* 4.将自定义的mapper和reducer通过job对象组装起来。
* @author xin,
*
*/
public class WordCount {
public static void main(String[] args) throws Exception {
//组装map和reduce
Job job = Job.getInstance(new Configuration());
//注意:main方法所在的类设置为参数
job.setJarByClass(WordCount.class);
//设置Mapper相关属性
job.setMapperClass(WCMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
//设置reduce相关属性
job.setReducerClass(WCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//true 打印出进度,false不打印出进度
job.waitForCompletion(true);
}
}
3、打成jar包,上传到linux 服务器上
4.执行
查看计算结果:
案例2:
手机只要上一次网就会产生一条数据,计算同一手机号在一段时间内的用的总流量(总流量 = 上行流量 + 下行流量)。
这个例子与上个例子相比有以下不同:1、使用了自定义对象序列化;2、将DCMapper和DCReducer作为DataCount的内部类,代码更加简洁紧凑。3、使用了Maven。
其他步骤相似,参看上例.
手机流量相关数据:
项目结构
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
/** 实现Writable,用于序列化和反序列化 */
public class DataBean implements Writable {
// 手机号
private String telNo;
// 上行流量
private long upPayLoad;
// 下行流量
private long downPayLoad;
// 总流量
private long totalPayLoad;
// 需要添加无参的构造方法,否则反射时会报错。
public DataBean() {
}
public DataBean(String telNo, long upPayLoad, long downPayLoad) {
this.telNo = telNo;
this.upPayLoad = upPayLoad;
this.downPayLoad = downPayLoad;
this.totalPayLoad = upPayLoad + downPayLoad;
}
public String getTelNo() {
return telNo;
}
public void setTelNo(String telNo) {
this.telNo = telNo;
}
public long getUpPayLoad() {
return upPayLoad;
}
public void setUpPayLoad(long upPayLoad) {
this.upPayLoad = upPayLoad;
}
public long getDownPayLoad() {
return downPayLoad;
}
public void setDownPayLoad(long downPayLoad) {
this.downPayLoad = downPayLoad;
}
public long getTotalPayLoad() {
return totalPayLoad;
}
public void setTotalPayLoad(long totalPayLoad) {
this.totalPayLoad = totalPayLoad;
}
// 反序列化
public void readFields(DataInput in) throws IOException {
this.telNo = in.readUTF();
this.upPayLoad = in.readLong();
this.downPayLoad = in.readLong();
this.totalPayLoad = in.readLong();
}
// 序列化
// 注意:1顺序,2类型Mapper<KEYIN,VALUEIN,KEYOUT,VALUEOUT>
public void write(DataOutput out) throws IOException {
// String类型序列化用writeUTF
out.writeUTF(telNo);
out.writeLong(upPayLoad);
out.writeLong(downPayLoad);
out.writeLong(totalPayLoad);
}
@Override
public String toString() {
return upPayLoad + "\t" + downPayLoad + "\t" + totalPayLoad;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
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.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class DataCount {
//入口程序
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(DataCount.class);
job.setMapperClass(DCMapper.class);
//当Mapper中的输出的key和value的类型和Reduce输出的key和value的类型相同时,以下两句可以省略。
/* job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(DataBean.class);*/
FileInputFormat.setInputPaths(job, new Path(args[0]));
job.setReducerClass(DCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DataBean.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
/**Mapper<KEYIN,VALUEIN,KEYOUT,VALUEOUT> KEYIN 这边没有用到,所以写LongWritable或者Text都可以,
*因为处理的是文本, VALUEIN就是文本的一行。输出是按手机号分组,KEYOUT是Text。定义一个DataBean,输出的value保存在DataBean中 **/
public static class DCMapper extends Mapper<LongWritable,Text,Text,DataBean> {
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] fields = line.split("\t");
long up = Long.parseLong(fields[8]);
long down = Long.parseLong(fields[9]);
context.write(new Text(fields[1]), new DataBean(fields[1], up, down));
}
}
public static class DCReducer extends Reducer<Text, DataBean, Text, DataBean> {
@Override
protected void reduce(Text keyIn, Iterable<DataBean> valuesIn, Context context)
throws IOException, InterruptedException {
long upSum = 0;
long downSum = 0;
for(DataBean value : valuesIn) {
upSum += value.getUpPayLoad();
downSum += value.getDownPayLoad();
}
context.write(keyIn, new DataBean(keyIn.toString(), upSum, downSum));
}
}
}
- mapreduce(六):MapReduce原理
- hadoop学习笔记六 --- mapreduce原理笔记
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- mapreduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- MapReduce原理
- mapreduce原理
- MapReduce原理
- pod计算资源管理
- 画图板
- Java知识点集锦
- 1try
- Mobile Service_纪中1327_dp
- mapreduce(六):MapReduce原理
- Java 快速读取文本文件最后一行数据
- SQL_Oracle 表中ID字段的自动递增
- Highcharts基础教程(一):Highcharts 的主要组成
- XML、JSON及其解析详解
- gvim配置(Windows版)
- CentOS 7 上部署Mono 4 和Jexus 5.6
- 设计模式之单例模式
- log4j基础详解及示例