Hadoop入门系列1——安装,配置,编程,部署和运行中已经安装并配置了基于伪分布模式的Hadoop平台
之后将介绍Hadoop编程。
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
/**
* 1.新版API中Mapper与Reducer已经不是接口而是抽象类
* 而且map函数与reduce函数也已经不再实现Mapper和Reducer接口
* 而是继承Mapper和Reducer抽象类
*
* 2.新版API广泛使用context对象,并使用MapContext进行MapReduce间的通信
* MapContext同时充当OutputCollector和Reporter的角色
*
* 3.Job的配置统一由Configuration来完成,而不必额外使用JobConf对守护进程进行配置
*
* 4.由Job类来负责Job的控制,而不是JobClient,JobClient在新的API中已经被删除
* @author wjm
*
*/
public class WordCount extends Configured implements Tool{
/**
* Map类是通过继承抽象类Mapper得到的类
* LongWritable,IntWritable,Text均是Hadoop中实现的用于封装Java数据类型的类
* 它们能够被串行化从而便于在分布式环境中进行数据交换,可以认为是long,int,String的替代品
* @author wjm
*
*/
public static class Map extends Mapper<LongWritable, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);//可以将IntWritable视为int类型
private Text word = new Text();//将Text视为String类型,其中word代表的是输入的文本
//自定义的map函数,形式如 void map(K1 key, V1 value, Context context)
//新版API使用Context对象同时充当原版API中OutputCollector与Reporter的角色,在MapReduce之间进行通信
//map函数将输入读入后切出其中的单词,并标记它的数目为1,形成<word,1>的形式
//并通过context进行保存和信息的传递
public void map(LongWritable key, Text value, Context context )throws IOException, InterruptedException{
String line = value.toString();//value是Text类型,即String类型。line是由value转换成的字符串
StringTokenizer tokenizer = new StringTokenizer(line);//按照line的内容进行切分
while(tokenizer.hasMoreTokens()){
word.set(tokenizer.nextToken());//word是输入的文本,按照tokenizer进行切分
context.write(word, one);
}
}
}
/**
* Reduce类是通过继承抽象类Reducer而形成的类
* @author wjm
*
*/
public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable>{
/**
* reduce函数接受context传递的信息(形如<word,1>的键值对),reduce将相同key
* 也就是word的值(即计数)收集起来,形成<word,list of 1>的形式
* 之后将这些1值加起来,即为单词的个数
*/
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException{
int sum = 0;
for(IntWritable val : values){
sum += val.get();
}
context.write(key, new IntWritable(sum));
}
}
@Override
public int run(String[] args) throws Exception {
/**
* 原版API使用JobConf类进行map reduce的配置
* 而新版API的Job的配置完全是由Configuration配置的
*/
Job job = new Job(getConf());//Job的初始化,Configuration类的getConf()方法对Job进行配置
job.setJarByClass(WordCount.class);
job.setJobName("wordcount");//setJobName()命名job,设置一个用户自定义的job名称
job.setOutputKeyClass(Text.class);//为job的输出数据设置Key类
job.setOutputValueClass(IntWritable.class);//为job的输出设置Value类
//Map和Reduce都是用户自定义的通过继承抽象类而得到的类
job.setMapperClass(Map.class);//为job设置Mapper类
job.setReducerClass(Reduce.class);//为job设置Reducer类
//InputFormat描述map-reduce中对job的输入定义
//OutputFormat描述map-reduce中对job的输出定义
//TextInputFormat是Hadoop默认的输入法,每个文件都会单独作为map输入
//TextOutputFormat是Hadoop默认的输出法,将每条记录以一行的形式存入文本文件
job.setInputFormatClass(TextInputFormat.class);//为map-reduce任务设置InputFormat实现类
job.setOutputFormatClass(TextOutputFormat.class);//为map-reduce任务设置OutputFormat实现类
//setInputPaths()为map-reduce job设置路径数组作为输入列表
//setOutputPaths()为map-reduce job设置路径数组作为输出列表
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
boolean success = job.waitForCompletion(true);
return success ? 0 : 1;
}
//main函数
public static void main(String[] args) throws Exception {
int ret = ToolRunner.run(new WordCount(), args);
System.exit(ret);
}
}