WordCountMR的主程序:
package wc;
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.LongWritable;
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.output.FileOutputFormat;
public class WordCountMR {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://hadoop01:9000");
System.setProperty("HADOOP_USER_NAME", "hadoop");
// conf.set("fs.defaultFS", "file:///");
System.out.println(conf.get("fs.defaultFS"));
/**
* 获取一个job对象。一个job就是一个完整的MapReduce App 应用程序
*
* job 一次工作
* task 一次任务
*/
Job job = Job.getInstance(conf);
job.setJarByClass(WordCountMR.class);
/**
* 设置该job的mapper和reducer组件
*/
job.setMapperClass(WCMapper.class);
job.setReducerClass(WCReducer.class);
/**
* 为什么要设置 job mapper组件的输出的key-value的类型?
*
* 因为 泛型 存在着 擦除的概念。
*
* 泛型只在编译生效
* 编译好了之后的class文件中,是压根没有泛型的概念
*
* 因为既然在网络传输过程当中存在序列化的操作, 那么要存在反操作, 反序列化操作
*
* 为什么只需要指定输出的泛型 , 而不是指定输入的泛型?
*
* 因为mapper组件的输入的key-value的类型是跟随 数据读取组件来决定
*
* 现在的wordcount程序的默认数据读取组件是 TextInputFormat和LineRecordReader
* 他们已经指定好了 mapper组件的输入的key-value的类型是 LongWritable和Text
*/
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
/**
* 为什么不要指定 reducer的输入的key-value类型?
*
* 因为reducer组件的输入的key-value的类型是由mapper组件的输出决定。
*/
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
/**
* 设置reduceTsk的个数
*
* 1、没有设置, 默认运行 1 个 reduceTask
*
* 2、在默认的HashPartitioner的规则之下, reduceTask的个数可以随意设置。
*
* 3、如果自定义了 Paritioner组件, 那么设置 redcueTASK的个数就必须结合具体的业务
*
* 4、设置reduceTask为0 个。。那就表示 根本就没有reducer阶段。
* 所以mapper阶段的数据直接输出 成为最终的结果
*
* 5 、如果没有设置reducer组件,又没有指定reducetask的个数,那就表示 会运行一个reduceTask运行默认的实现。
*
* 第四种方式和第五种方式:
* 所有maptask的结果数据都被原样输出。
* 但是又不一样的地方: 如果没有reducer阶段,那就表示,mapreduce程序不会对key-value进行排序
* 就表示没有 mapper和reducer中间的shuffle阶段
*
*/
job.setNumReduceTasks(3); //reduceTask的编号是: 0 1 2
// 如果有某一个值的getPartition方法的返回值不在这个取值范围之内,就会报错。!!!
/**
* 指定该job的输入输出数据的路径
*/
FileInputFormat.setInputPaths(job,args[0]);
Path outputPath = new Path(args[1]);
FileSystem fs = FileSystem.get(conf);
if(fs.exists(outputPath)){
fs.delete(outputPath, true);
}
FileOutputFormat.setOutputPath(job, outputPath);
/**
* 提交任务去运行
*
* 提交任务到YARN集群去运行
*
* 提交的方法可以是:
* job.submit();
* job.waitForComplrtion(true);
*
* true : 是否打印执行过程
*
* 当前这个方式是一个阻塞方法。
*
*/
boolean isDone = job.waitForCompletion(true);
/**
* 这是退出整个MapReduce程序的命令
*
* 如果传参为0,表示正常退出。
*/
System.exit(isDone ? 0 : 1);
}
}
WCMapper:
package wc;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class WCMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
/**
* key: 当前的value这一行在当前整个文件中的起始偏移量
*
* value: 就是默认的数据读取组件每次读取的一行数据
*/
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
/**
* 输出的key-value
* 输出的key-value的含义: 某个单词出现了几次
* 某个特定的单词出现了 一次
* key : word
* value : 1
*/
String[] split = value.toString().split(" ");
for(String word : split){
context.write(new Text(word), new IntWritable(1));
}
}
}
WCReducer:
package wc;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
/**
* 当前的WCMApper组件就是WordCountMR中的第一个阶段的reduce函数的定义之地
*
* 四个泛型的含义:
* 前一对: reduce组件输入的key-value的类型
*
* 后一对: reduce组件输出的key-value的类型
*
*/
/**
* reducer组件的输入key-value的类型必须和mapper组件的输出的key-value类型要一致
*
*/
public class WCReducer extends Reducer<Text, IntWritable, Text, LongWritable>{
/**
* reduce方法的参数:
* key : 单词
* values : 单词的次数的一个集合(是唯一的一个所有的相同单词的value集合)
*
* key : hello
* values (1,1,1,1,1)
* reduce方法每执行一次,接收到的参数,就是所有key相同的key-value的集合
*
* 每次shuffle过程处理完了一个key所对应的所有的key-value之后,就会调用reducer组件的reduce方法执行一次 聚合操作(业务逻辑)
*
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int sum = 0;
for(IntWritable value : values){
sum += value.get();
}
//输出结果
context.write(key, new LongWritable(sum));
}
}