您的位置:首页 > 编程语言 > Java开发

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 个。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: