前一篇博客讲述了如何进行Hadoop坏境的搭建,以及第一个传输文件程序的编写,通过第一个文件可能大概对Hadoop有一个了解了,但是Hadoop的精髓在于mapreduce,下面我们就来看看如何编写Hadoop的第一个“hello world”程序--也就是WordCount程序。
有很多的博客讲述Wordcount是什么,但是没有对里面的代码进行详细讲解,导致很多的入门者卡在了这块。
1、MapReduce工作流程
mapreduce是如何让工作的,以Wordcount为例,首先mapreduce分为两个阶段,分别是map阶段和reduce阶段,其中比如我们的data文件是这样的,一共三行。
hadoop is a good tool
python is a good language
python is a bad language
map阶段:主要为分别读取文件的每一行,然后进行单词的划分,形成<key,value>对,eg: <hadoop,1>,<is,1>........等等这样的形式,然后进行发送,reduce进行接收,自至于发送的细节,这都是Hadoop封装好的。
reduce阶段:因为当我们有一个较大的文件时,会启动多个map节点,因此reduce会接到多个map发过来的数据,并且自动将相同的key进行整合,所以reduce接到的就是这样形式的<key,iter<value,value,...>, eg: <hadoop,1>,<is,{1,1,1}>,<python,{1,1}>....这样形式。因此reduce程序只需要做的就是讲这些iter里面的value进行累加。
所以很简单的逻辑,下面是实现的具体过程和代码。
2、总体架构
总体分别三个文件一个map.class(map逻辑处理程序) 一个reduce.class(reduce逻辑处理程序) 一个runner.class(老大,调配前面两个程序,并进行job的提交)
提前准备配置:右键工程在property里面的java build path里面添加我们需要的jar包,否则后边无法进行,并且最好讲配置好的core-site.xml hdfs-site.xml文件添加进行,以供conf进行加载。
3、map逻辑处理程序
package wyk_firsthadoop;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import io.netty.util.internal.StringUtil;
//其中LongWritable和Text对应着long和string,这里就是Hadoop定义的一种序列化的类型,方便在网络间//进行传输
//继承来自Hadoop的mapper类,四个参数分别为keyin.valuein,keyout,valueout.这里仅仅是定义其类型
public class WCMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
@Override
//map接受的参数,key value,和一个配置参数
protected void map(LongWritable key, Text value,Context context)
throws IOException, InterruptedException {
//将Text转到string格式
String line=value.toString();
//line就是data中的一行,接下来进行切分
String[] words=StringUtil.split(line,' ');
//context.write将我们的统计信息发送出去,reduce进行接收,还是键值对的形式
for(String word : words) {
//new Text(word)将string再次转换为Text格式
context.write(new Text(word), new LongWritable(1));
}
}
}
4、reduce逻辑处理程序
package wyk_firsthadoop;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
//还是继承来自Hadoop的reducer类,其中四个参数分别为keyin,valuein,keyout,valueout.
public class WCReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
@Override
//这里的参数分别为value就是前面说的是一个迭代器
protected void reduce(Text key, Iterable<LongWritable> values,
Context context) throws IOException, InterruptedException {
//声明一个计数器
long count =0;
//进行累加
for(LongWritable value:values) {
count+=value.get(); //get方法是进行数据类型的转换to long
}
//继续调用context.write,进行发送
context.write(key, new LongWritable(count));
}
}
5、总体调配程序
mapreduce的提交需要以job形式来提交,前面定义好的map reduce的处理逻辑,那么如何运行这两个程序呢?怎么才能及交给Hadoop?这里就要统一进行调配,形成job提交,并运行。
package wyk_firsthadoop;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.Job;
public class runner {
public static void main(String[] args) throws IOException, Exception, InterruptedException {
// mapreduce的提交需要以job形式来提交,前面定义好的mapreduce的处理逻辑,这里
//就要统一进行调配,形成job提交,病运行
Configuration conf=new Configuration();
//创建job实例
Job job=Job.getInstance(conf);
//设置生成的jar包 所对应的文件
job.setJarByClass(runner.class);
//设置哪一个是map处理程序,哪一个是reduce程序
job.setMapperClass(WCMapper.class);
job.setReducerClass(WCReducer.class);
//设置输出数据的格式,这里分别设置了key value的格式
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//设置输入数据的路径,文件位于HDFS的根目录下
FileInputFormat.setInputPaths(job, new Path("/test_data.txt"));
//设置输出文件的路径,注意这里一定要是不存在的文件夹,不然会报错-已存在
FileOutputFormat.setOutputPath(job, new Path("/wyk_out"));
//提交job,并运行
job.waitForCompletion(true);
}
}
6、NEXT
(1)将上述的程序所在的工程打成一个jar包。
(2)自己写一个简单的data文件,并上传至HDFS,命令:hadoop fs -put xxxx.txt / 放置根目录下
(3)运行haoop jar worcount_wyk.jar wyk_firsthadoop.runner
(4)查看输出结果 hadoop fs -cat /wyk_out/part-5-0000 大家视情况而定,可能文件名不一样
输出结果类似:
hadoop 1
python 2
is 3
tool 1
.....