hadoop入门(六)JavaAPI+Mapreduce实例wordCount单词计数详解
2017-08-13 22:40
901 查看
刚刚研究了一下haoop官网单词计数的例子,把详细步骤解析贴在下面:
准备工作:
1、haoop集群环境搭建完成
2、新建一个文件hello,并写入2行单词,如下:
[root@CentOS hadoop-2.6.0]# vi hello
hello you
hello me
3、把文件传到hdfs根目录下:
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -put hello /
查看文件是否导入成功
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -text /hello
hello you
hello me
4、打开eclipse,如果你之前练过JavaAPI操作hdfs,那么在原来项目中直接添加MapReduce的jar包就行了,Maven项目直接添加依赖就好。
jar包在之前搭建Windows的hadoop环境下的C:\Program Files (x86)\hadoop-2.6.3\share\hadoop\mapreduce下的所有jar和此文件夹下lib中的所有jar.
如果之前没做过hdfs练习,那么其他jar请参考我的前几篇文章Hadoop入门(三)。
准备工作做好了,下面就开始编码,代码中我注释的很详细:
◆执行步骤:
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、values处理,转换成新的key、value输出。
2.3 把reduce的输出保存到文件中。
package test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
public class WordCountApp {
/**我定义一个内部类MyMapper继承Mapper类
* 泛型解释:LongWritable是大数据里的类型对应java中的Long类型
* Text对应java里的String类型,所以Mapper泛型前2个就是LongWritable, Text
* 逻辑解释:由于我们做的是单词计数,文件中的单词是下面2行
* hello you
* hello me
* 所以 ,根据上面
* 步骤1.1,则 <k1,v1>是<0, hello you>,<10,hello me> 形式
* 文件的读取原则:<每行起始字节数,行内容>,所以第一行起始字节是0,内容是hello you
* 第二行起始字节是10,内容是hello me,从而得出k1,v1
* 步骤1.2:如果我们要实现计数,我们可以把上面的形式通过下面的map函数转换成这样
* <k2,v2>---> <hello,1><you,1><hello,1><me,1>
* 于是Mapper泛型后2个就是Text,LongWritable
*可以理解泛型前2个为输入的map类型,后2个为输出的map类型
*/
public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
//定义一个k2,v2
Text k2 = new Text();
LongWritable v2 = new LongWritable();
@Override
//下面的key就是从文件中读取的k1,value就是v1,map函数就是在执行步骤1.2
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
String[] words = value.toString().split("\t");
for (String word : words) {
//word表示每一行中的每个单词,即k2
k2.set(word);
v2.set(1L); //没排序分组前每个单词都是1个,由于是Long类型所以加L
context.write(k2, v2);//写出
}
}
}
//步骤1.3:对输出的所有的k2、v2进行分区去执行MapperTask
//步骤1.4:shuffle-排序后的结果是<hello,1><hello,1><me,1><you,1>
// 分组后的结果是<hello,{1,1}><me,{1}><you,{1}>
//1.3和1.4,1.5是hadoop自动帮我们做的,我们做的就是上面写的map函数的输出逻辑
/**
* 下面这个MyReducer函数是输出<k3,v3>的函数,逻辑要我们自己写。
* 传入的参数是上面得到的<hello,{1,1}><me,{1}><you,{1}>
* 把这些map分给不同的ReducerTask去完成最后
* 输出为<k3,v3>是<hello, 2>,<me, 1>,<you, 1>
*/
public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
LongWritable v3 = new LongWritable();
@Override
//传入的数据形如<hello,{1,1}>,V的值是个集合,所以这里Iterable<LongWritable>
protected void reduce(Text k2, Iterable<LongWritable> v2s,
Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
long count = 0L;
for (LongWritable v2 : v2s) {
count += v2.get();
}
v3.set(count);
//k2就是k3,都是一个单词
context.write(k2, v3);
}
}
public static void deleteOutDir(Configuration conf, String OUT_DIR)
throws IOException, URISyntaxException {
FileSystem fileSystem = FileSystem.get(new URI(OUT_DIR), conf);
if(fileSystem.exists(new Path(OUT_DIR))){
fileSystem.delete(new Path(OUT_DIR), true);
}
}
/**
* 上面我们把map,reduce都写完了,下面我们把它们合在一起,运转起来
*/
public static void main(String[] args) throws Exception {
//加载驱动
Configuration conf = new Configuration();
//获取job,告诉他需要加载那个类
Job job = Job.getInstance(conf, WordCountApp.class.getSimpleName());
//如果文件达成jar包在hadoop运行必须做这个设置
job.setJarByClass(WordCountApp.class);
//获取文件数据
FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.19.128:9000/hello"));
//通过TextInputFormat把读到的数据处理成<k1,v1>形式
job.setInputFormatClass(TextInputFormat.class);
//job中加入Mapper,同时MyMapper类接受<k1,v1>作为参数传给类中map函数进行数据处理
job.setMapperClass(MyMapper.class);
//设置输出的<k2,v2>的数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//job中加入Reducer,Reducer自动接收处理好的map数据
job.setReducerClass(MyReducer.class);
//设置输出的<k3,v3>的数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//设置输出目录文件out1
String OUT_DIR = "hdfs://192.168.19.128:9000/out1";
FileOutputFormat.setOutputPath(job, new Path(OUT_DIR));
job.setOutputFormatClass(TextOutputFormat.class);
//如果这个文件存在则删除,如果文件存在不删除会报错。
deleteOutDir(conf, OUT_DIR);
//把处理好的<k3,v3>的数据写入文件
job.waitForCompletion(true);
}
}
编码完事了,建议不要再eclipse环境下运行,经常会出现很多错误。我们打成jar在linux的hadoop环境下运行
1、打jar包
在WordCountApp文件上右键——》Export——》JAR file ——》next ——》在下面选个存放路径并命名——》next——》next——》Main class选择main函数所在的类也就是WprdCountApp这个类——》finish
2、把生成的wordCount.jar导入到虚拟机,我直接导入到hadoop的安装目录下了 /usr/local/hadoop/hadoop-2.6.0/
3、在hadoop环境下执行这个jar
[root@CentOS hadoop-2.6.0]# bin/hadoop jar wordCount.jar
................
.................
17/08/13 10:39:41 INFO mapred.LocalJobRunner: Finishing task: attempt_local730042948_0001_r_000000_0
17/08/13 10:39:41 INFO mapred.LocalJobRunner: reduce task executor complete.
17/08/13 10:39:41 INFO mapreduce.Job: map 100% reduce 100%
17/08/13 10:39:41 INFO mapreduce.Job: Job job_local730042948_0001 completed successfully
17/08/13 10:39:42 INFO mapreduce.Job: Counters: 38
....................
..........................
看到上面的结果说明程序执行完毕map 100% reduce 100%
4、我们来看一下是否生成out1文件,再看看文件内容
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -ls /
-rw-r--r-- 3 root supergroup 19 2017-08-13 08:37 /hello
drwxr-xr-x - root supergroup 0 2017-08-13 10:39 /out1
看看out1里面有啥:
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -ls /out1
-rw-r--r-- 3 root supergroup 0 2017-08-13 10:39 /out1/_SUCCESS
-rw-r--r-- 3 root supergroup 19 2017-08-13 10:39 /out1/part-r-00000
我们打开part-r-0000看看里面:
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -text /out1/part-r-00000
hello 2
me 1
you 1
好了,单词计数完成,hello2 个,me 1个,you 1 个。
准备工作:
1、haoop集群环境搭建完成
2、新建一个文件hello,并写入2行单词,如下:
[root@CentOS hadoop-2.6.0]# vi hello
hello you
hello me
3、把文件传到hdfs根目录下:
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -put hello /
查看文件是否导入成功
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -text /hello
hello you
hello me
4、打开eclipse,如果你之前练过JavaAPI操作hdfs,那么在原来项目中直接添加MapReduce的jar包就行了,Maven项目直接添加依赖就好。
jar包在之前搭建Windows的hadoop环境下的C:\Program Files (x86)\hadoop-2.6.3\share\hadoop\mapreduce下的所有jar和此文件夹下lib中的所有jar.
如果之前没做过hdfs练习,那么其他jar请参考我的前几篇文章Hadoop入门(三)。
准备工作做好了,下面就开始编码,代码中我注释的很详细:
◆执行步骤:
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、values处理,转换成新的key、value输出。
2.3 把reduce的输出保存到文件中。
package test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
public class WordCountApp {
/**我定义一个内部类MyMapper继承Mapper类
* 泛型解释:LongWritable是大数据里的类型对应java中的Long类型
* Text对应java里的String类型,所以Mapper泛型前2个就是LongWritable, Text
* 逻辑解释:由于我们做的是单词计数,文件中的单词是下面2行
* hello you
* hello me
* 所以 ,根据上面
* 步骤1.1,则 <k1,v1>是<0, hello you>,<10,hello me> 形式
* 文件的读取原则:<每行起始字节数,行内容>,所以第一行起始字节是0,内容是hello you
* 第二行起始字节是10,内容是hello me,从而得出k1,v1
* 步骤1.2:如果我们要实现计数,我们可以把上面的形式通过下面的map函数转换成这样
* <k2,v2>---> <hello,1><you,1><hello,1><me,1>
* 于是Mapper泛型后2个就是Text,LongWritable
*可以理解泛型前2个为输入的map类型,后2个为输出的map类型
*/
public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
//定义一个k2,v2
Text k2 = new Text();
LongWritable v2 = new LongWritable();
@Override
//下面的key就是从文件中读取的k1,value就是v1,map函数就是在执行步骤1.2
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
String[] words = value.toString().split("\t");
for (String word : words) {
//word表示每一行中的每个单词,即k2
k2.set(word);
v2.set(1L); //没排序分组前每个单词都是1个,由于是Long类型所以加L
context.write(k2, v2);//写出
}
}
}
//步骤1.3:对输出的所有的k2、v2进行分区去执行MapperTask
//步骤1.4:shuffle-排序后的结果是<hello,1><hello,1><me,1><you,1>
// 分组后的结果是<hello,{1,1}><me,{1}><you,{1}>
//1.3和1.4,1.5是hadoop自动帮我们做的,我们做的就是上面写的map函数的输出逻辑
/**
* 下面这个MyReducer函数是输出<k3,v3>的函数,逻辑要我们自己写。
* 传入的参数是上面得到的<hello,{1,1}><me,{1}><you,{1}>
* 把这些map分给不同的ReducerTask去完成最后
* 输出为<k3,v3>是<hello, 2>,<me, 1>,<you, 1>
*/
public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
LongWritable v3 = new LongWritable();
@Override
//传入的数据形如<hello,{1,1}>,V的值是个集合,所以这里Iterable<LongWritable>
protected void reduce(Text k2, Iterable<LongWritable> v2s,
Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
long count = 0L;
for (LongWritable v2 : v2s) {
count += v2.get();
}
v3.set(count);
//k2就是k3,都是一个单词
context.write(k2, v3);
}
}
public static void deleteOutDir(Configuration conf, String OUT_DIR)
throws IOException, URISyntaxException {
FileSystem fileSystem = FileSystem.get(new URI(OUT_DIR), conf);
if(fileSystem.exists(new Path(OUT_DIR))){
fileSystem.delete(new Path(OUT_DIR), true);
}
}
/**
* 上面我们把map,reduce都写完了,下面我们把它们合在一起,运转起来
*/
public static void main(String[] args) throws Exception {
//加载驱动
Configuration conf = new Configuration();
//获取job,告诉他需要加载那个类
Job job = Job.getInstance(conf, WordCountApp.class.getSimpleName());
//如果文件达成jar包在hadoop运行必须做这个设置
job.setJarByClass(WordCountApp.class);
//获取文件数据
FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.19.128:9000/hello"));
//通过TextInputFormat把读到的数据处理成<k1,v1>形式
job.setInputFormatClass(TextInputFormat.class);
//job中加入Mapper,同时MyMapper类接受<k1,v1>作为参数传给类中map函数进行数据处理
job.setMapperClass(MyMapper.class);
//设置输出的<k2,v2>的数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//job中加入Reducer,Reducer自动接收处理好的map数据
job.setReducerClass(MyReducer.class);
//设置输出的<k3,v3>的数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//设置输出目录文件out1
String OUT_DIR = "hdfs://192.168.19.128:9000/out1";
FileOutputFormat.setOutputPath(job, new Path(OUT_DIR));
job.setOutputFormatClass(TextOutputFormat.class);
//如果这个文件存在则删除,如果文件存在不删除会报错。
deleteOutDir(conf, OUT_DIR);
//把处理好的<k3,v3>的数据写入文件
job.waitForCompletion(true);
}
}
编码完事了,建议不要再eclipse环境下运行,经常会出现很多错误。我们打成jar在linux的hadoop环境下运行
1、打jar包
在WordCountApp文件上右键——》Export——》JAR file ——》next ——》在下面选个存放路径并命名——》next——》next——》Main class选择main函数所在的类也就是WprdCountApp这个类——》finish
2、把生成的wordCount.jar导入到虚拟机,我直接导入到hadoop的安装目录下了 /usr/local/hadoop/hadoop-2.6.0/
3、在hadoop环境下执行这个jar
[root@CentOS hadoop-2.6.0]# bin/hadoop jar wordCount.jar
................
.................
17/08/13 10:39:41 INFO mapred.LocalJobRunner: Finishing task: attempt_local730042948_0001_r_000000_0
17/08/13 10:39:41 INFO mapred.LocalJobRunner: reduce task executor complete.
17/08/13 10:39:41 INFO mapreduce.Job: map 100% reduce 100%
17/08/13 10:39:41 INFO mapreduce.Job: Job job_local730042948_0001 completed successfully
17/08/13 10:39:42 INFO mapreduce.Job: Counters: 38
....................
..........................
看到上面的结果说明程序执行完毕map 100% reduce 100%
4、我们来看一下是否生成out1文件,再看看文件内容
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -ls /
-rw-r--r-- 3 root supergroup 19 2017-08-13 08:37 /hello
drwxr-xr-x - root supergroup 0 2017-08-13 10:39 /out1
看看out1里面有啥:
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -ls /out1
-rw-r--r-- 3 root supergroup 0 2017-08-13 10:39 /out1/_SUCCESS
-rw-r--r-- 3 root supergroup 19 2017-08-13 10:39 /out1/part-r-00000
我们打开part-r-0000看看里面:
[root@CentOS hadoop-2.6.0]# bin/hdfs dfs -text /out1/part-r-00000
hello 2
me 1
you 1
好了,单词计数完成,hello2 个,me 1个,you 1 个。
相关文章推荐
- Hadoop入门概述-概念及WordCount实例详解
- MapReduce入门级之WordCount单词计数
- Hadoop MapReduce编程 API入门系列之wordcount版本2(六)
- Hadoop MapReduce编程 API入门系列之wordcount版本4(八)
- 知识学习——Hadoop MapReduce开发入门程序WordCount详解
- (HADOOP入门)mapreduce入门程序wordcount旧版API
- Hadoop入门实例——WordCount统计单词
- Hadoop MapReduce编程 API入门系列之wordcount版本5(九)
- hadoop入门(WordCount实例详解)
- Hadoop MapReduce编程 API入门系列之wordcount版本1(五)
- Hadoop MapReduce编程 API入门系列之wordcount版本3(七)
- mapreduce入门之wordcount注释详解
- hadoop实例分析之WordCount单词统计分析
- hadoop集群配置方法---mapreduce应用:xml解析+wordcount详解---yarn配置项解析
- hadoop2.5的第一个HelloWorld程序—单词计数(WordCount.)
- hadoop MapReduce实例解析(WordCount)
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- Hadoop入门—Linux下伪分布式计算的安装与wordcount的实例展示
- Hadoop之道--MapReduce之Hello World实例wordcount