MapRedue的思想“分而治之”
MapReduce是Hadoop提供的一套分布式并行计算框架,通过键值对<key,value>进行数据传输
MapReduce框架为每个提交集群的Job(作业),通过计算InputSplit(切分),来分配map task
两个阶段:
- Map(映射或并行阶段)
Map是将输入记录转换为中间记录,转换后的中间记录不必与输入记录的类型相同。给定的输入对可以映射为零或许多输出对 - Reduce(归约或合并阶段)
对map阶段的结果进行汇总
例题
在hadoop集群有两个文件:text1、text2,求出文件中词频出现的次数
- text1文件内容:
hello world ni hao
how are you
I am fine ni hao - text2文件内容:
hello world the best
ni hao
ha hello
构建思路
-
构造数据并上传到hdfs
-
创建Mapper类,继承 org.apache.hadoop.mapreduce.Mapper
Map阶段操作的输入输出键值对为<k1,v1> <k2,v2>
k1值为偏移量,在本例题中类型为LongWritable;v1值是行内容,在本例题中类型为Text;k2是中间值,v2业务值例如text1中:
hello world ni hao 输入<k1,v1>:<0,‘hello world ni hao’> 输出 <k2,v2>:<hello,1>,<world,1>,<ni,1>,<hao,1>
how are you 输入 <k1,v1>:<18,‘how are you’> 输出 <k2,v2>:<how,1>,<are,1>,<you,1>
ha hello 输入 <k1,v1>:<29,‘ha hello’> 输出 <k2,v2>:<ha,1>,<hello,1> -
创建Reducer类,并继承 org.apache.hadoop.mapreduce.Reducer
reduce阶段的键值对:输入:<k2,v2> 输出:<k3,v3>
输入键值对类型<k2,v2>一定要与Mapper阶段的输出键值对<k2,v2>类型一致 -
创建Job类,设置所必须的参数,最后进行集群的提交
-
将编辑好的类进行*打包*,【export】–> 【jar file】 --> 包名.jar
-
选择在集群进行*提交job*,或者在eclipse进行提交集群
提交集群时遇到异常的解决
-
在集群直接运行jar文件
a) 遇到了创建的文件夹权限问题
修改权限:hadoop -fs -chmod -R 777 /
b) 集群上运行jar文件的命令:hadoop jar *.jar文件路径 【主类全名】 输入text1、text2文件路径 输出结果文件路径
在打包时如果设置了主类则可省略
c) 在yarn-site.sh文件中配置yarn.resourcemanager.hostname
属性,将值设置为你的主机名 -
在eclipse上提交Job至集群
a) 要将yarn-site.xml、mapred-site.xml、core-site.xml、hdfs-site.xml文件在classpath下,即src下
b) 在运行时集群上自动创建了tmp目录,导致了权限问题,进而要修改tmp目录的权限
c) 解决用户可以提交跨平台的应用程序,即从Windows客户端提交应用程序到Linux/Unix服务器
修改mapred-site.xml中的mapreduce.app-submission.cross-platform
属性,值为true
d) 解决类找不到的问题,将导出的jar包引入classpath中
e) 解决0.0.0.0:10020异常,在集群上打开历史服务器
命令语句:mr-jobhistory-daemon.sh start historyserver
实现代码
WordCountMapper.class
package com.dragon.hadoop.mr;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
/*
* 映射器,并行处理
* <k1,v1> <k2,v2>
*/
public class WordCountMapper extends Mapper<LongWritable,Text, Text, IntWritable>{
private Text word=new Text();
private IntWritable one=new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
//按空格进行切分,再将值转换为String
//StringTokenizer st=new StringTokenizer(value.toString());
String line =value.toString();
String[] words =line.split(" ");
for (String wd : words) {
//System.out.println(wd);
//context.write(new Text(wd), new IntWritable(1));
//由于循环一次就要new一次,造成内存负荷太大,上面两行改为:
word.set(wd);
context.write(word, one);
System.out.println(word);
}
}
}
WordCountReducer.class
package com.dragon.hadoop.mr;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
/*
* 规约器,合并处理
* <k2,v2> <k3,v3>
* rudece方法参数 key:k2 Iterable<IntWritable>:<v2,v2,v2,v2>迭代器数组
* <k2,Iterable<v2>>:按照词组进行分组,把相同的key值放到vlaues中,例如<hello,<1,1>>
*
*/
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
private IntWritable _sum=new IntWritable();
@Override
//Reducer规约器的输入键值对类型<k2,v2>一定要与Mapper阶段的输出键值对<k2,v2>类型一致
protected void reduce(Text key, Iterable<IntWritable> values,
Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
int sum=0;
//遍历迭代器数组,得到每个词组出现的次数
for (IntWritable value : values) {
sum+=value.get();//词组出现的次数求和 v3
}
//context.write(new Text(key), new IntWritable(sum));
//负荷太大,可以转为为下面代码:
_sum.set(sum);
context.write(key,_sum);//输出词组和出现的次数和
}
}
WordCount.class
package com.dragon.hadoop.mr;
import java.io.IOException;
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;
public class WordCount {
public static void main(String[] args) throws ClassNotFoundException, InterruptedException {
Configuration conf =new Configuration();
conf.set("mapreduce.app-submission.cross-platform", "true");//解决windows提交跨平台问题,修改此属性
try {
//创建一个作业,设置它的参数,再提交到集群
Job job =Job.getInstance(conf);
//通过主类全名搜索对应的jar包
job.setJarByClass(WordCount.class);
/*
* 第二种方式设置加载的包:
* job.setJar("wc.jar");
* 打包后再将wc.jar包上传到hadoop集群的classpath下
*/
job.setMapperClass(WordCountMapper.class);//设置mapper类
job.setReducerClass(WordCountReducer.class);//设置reducer类
//设置job的名字
job.setJobName("wordcount");
//设置map阶段输出键值对的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//设置reduce阶段的输出键值对的类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置job工作的文件输入路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
//设置job的文件输出路径
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//提交集群,等待完成
System.exit(job.waitForCompletion(true) ? 0 : 1);
} catch (IOException e) {
e.printStackTrace();
}
}
}