文章目录
1. 前言
在上一节我已经详细了介绍了MapReduce的工作原理,为了加深对MapReduce的理解,我将以一个Hadoop界的hello world程序来示例。一个最简单的MapReduce应用程序至少包含三个部分:MAP、REDUCE、MAIN函数。下面我将做简要的设计分析。
2. WordCount实现设计分析
2.1 实例文件
创建一个wordcount.txt的文件并上传到HDFS中,话不多说,直接截图:
(简单的vim操作还是要会的)
2.2 Map过程
并行读取文本,对读取的单词进行map操作,每个词都以<key,value>形式生成。
读取第一行并分割单词,得到如下结果:
<Hello, 1> <World, 1> <Bye, 1> <World, 1>
读取第二行并分割单词,得到如下结果:
<Hello, 1> <Hadoop, 1> <Bye, 1> <Hadoop, 1>
读取第三行并分割单词,得到如下结果:
<Bye, 1> <Hadoop, 1> <Hello, 1> <Hadoop, 1>
2.3 Reduce过程
Reduce操作是对map的结果进行排序、合并,最后求出结果。
在Reduce阶段将根据相同的key组合成value数组。
<Bye, 1, 1, 1>
<Hadoop, 1, 1, 1, 1>
<Hello, 1 ,1 ,1>
<World, 1, 1>
然后循环统计,最后得到如下结果:
<Bye, 3>
<Hadoop, 4>
<Hello, 3>
<World, 2>
3. 本地环境配置
4. 代码实现
4.1 Map代码
package com.mapreduce.wordcount;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
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;
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
@Override
protected void map(LongWritable key, Text value, Context context)throws IOException, InterruptedException{
String[] str = value.toString().split(" ");
for(int i = 0;i < str.length; i++){
context.write(new Text(str[i]), new IntWritable(1));
}
}
}
4.2 Reduce代码
package com.mapreduce.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text arg0, Iterable<IntWritable> arg1, Context arg2) throws IOException, InterruptedException {
int sum = 0;
for(IntWritable i:arg1){
sum += i.get();
}
arg2.write(arg0, new IntWritable(sum));
}
}
4.3 main代码
package com.mapreduce.wordcount;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import java.net.URI;
public class WordCountRunJob {
private static final String INPUT_PATH = "hdfs://master001:9000/wordcount.txt";
private static final String OUTPUT_PATH = "hdfs://master001:9000/output";
public static void main(String[] args) throws Exception{
System.setProperty("HADOOP_USER_NAME", "hadoop");
Configuration conf = new Configuration();
//提升代码的健壮性
final FileSystem fileSystem = FileSystem.get(URI.create(INPUT_PATH), conf);
if(fileSystem.exists(new Path(OUTPUT_PATH))){
fileSystem.delete(new Path(OUTPUT_PATH), true);
}
Job job = Job.getInstance(conf, "WordCount");
//run jar class 主方法
job.setJarByClass(WordCountRunJob.class);
//设置map
job.setMapperClass(WordCountMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//设置reduce
job.setReducerClass(WordCountReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置input format
job.setInputFormatClass(TextInputFormat.class);
FileInputFormat.addInputPath(job, new Path(INPUT_PATH));
//设置output format
job.setOutputFormatClass(TextOutputFormat.class);
FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH));
//提交job
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
4.4 Bug分析
如果出现如下错误:
解决方式:
- 在windows/system32下放入hadoop/bin目录下的hadoop.dll和winutils.exe文件:
注意:需要重启后才能生效。 - 点击错误文件NativeIO.java 557
- 复制NativeIO代码,在本地项目新建org.apache.hadoop.io.nativeio包,然后在包下建立NativeIO类
- 将第557行代码改为return true
4.5 效果截图
5. WordCount代码说明
5.1 map方法
protected void map(LongWritable key, Text value, Context context
继承了Mapper类,实现map方法,这里有三个参数,前面两个LongWritable key, Text value就是输入的key和value,第三个参数Context context记录的是整个上下文。比如可以通过context将数据写出去。
5.2 reduce方法
protected void reduce(Text arg0, Iterable arg1, Context arg2)
继承Reducer类,实现reduce方法,reduce函数的输入也是是一个key/value的形式,不过它的value是一个迭代器的形式Iterable values,也就是说reduce的输入是一个key对应一组的value,reduce也有context,和map的context作用一致。
5.3 main函数
对于main函数:
创建Configuration类:运行MapReduce程序前都要初始化Configuration,该类主要是读取MapReduce系统配置信息。
创建Job类:
Job job = Job.getInstance(conf, "WordCount")
job.setJarByClass(WordCountRunJob.class)
job.setMapperClass(WordCountMapper.class)
job.setReducerClass(WordCountReducer.class)
第一行构建一个job,有两个参数,一个是conf,另外一个是这个jpb的名称。
第二行就是设置我们自己开发的MapReduce类。
第三行和第四行就是设置map函数和reduce函数的实现类。
设置输出类型:
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
第一行和第二行是设置map函数的输出类型,即reduce函数的输入类型。第三四行是定义输出的key/value的类型,也就是最终存储在HDFS上结果文件的key/value的类型。
设置路径:
//设置input format
job.setInputFormatClass(TextInputFormat.class);
FileInputFormat.addInputPath(job, new Path(INPUT_PATH));
//设置output format
job.setOutputFormatClass(TextOutputFormat.class);
FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH));
input format构建了输入的数据文件,output format构建了输出的数据文件。
运行成功后退出程序:
System.exit(job.waitForCompletion(true) ? 0 : 1);
6. 提交到远程集群
感兴趣的可以自己去尝试,这里给大家提供一个大体步骤:
- 将程序打包,然后上传到CentOS上
- 使用hadoop jar 命令将作业提交到集群运行
如果程序出现任何BUG欢迎下方留言讨论!!!