首先我们要知道,map阶段的map方法是读取一行就调用一次
package com.zhan.mapreduce.wordcount;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* @Classname WordCountMapper
* @Description MrZhan
* @Date 2021/10/7 10:42
* @Created by 1
*/
/**
* KEYIN:表示的是map阶段输入kv中的k类型 在默认组件下是 其实位置偏移量 LongWritable
* VALUEIN 表示ma阶段输入kv中的v类型 在默认组件下 是一行的内容 是Test
* todo MapReduce 有默认的读数据组件 叫做TextInputFormat
* // TODO: 2021/10/7 读数据的行为是:一行一行的读取数据
* k: 每一个行的起始的位置偏移量 通常没有意义
* v:这一行的文本内容
* KEYOUT
* VALUEOUT
*/
public class WordCountMapper extends Mapper<LongWritable, Text,Text,LongWritable> {
/**
*
* @param key
* @param value
* @param context
* @throws IOException
* @throws InterruptedException
*
*
*/
private Text outkey = new Text();
private final static LongWritable outvalue = new LongWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//todo 从程序的上下文环境中获取一个全局计数器 指定计数器所属组的名字 计数器的名字
Counter count = context.getCounter("com.zhan", "apple_count");
//拿去一行数据转换为String类型
String line= value.toString();
//根据分隔符进行切割
String[] split = line.split("[s+]");
//遍历数组
for (String s : split) {
//计数器的使用
if ("apple".equals(s)){
count.increment(1);
}
//输出数据:把每个文单词标记1 输出结果<单词,1>
//使用上下文对象
outkey.set(s);
context.write(outkey,outvalue);
}
}
}
掌握了每个阶段的key value值就能掌握mapreduce的代码编写
package com.zhan.mapreduce.wordcount;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* @Classname WordCountReducer
* @Description MrZhan
* @Date 2021/10/7 11:03
* @Created by 1
*/
public class WordCountReducer extends Reducer<Text, LongWritable,Text,LongWritable> {
/**
*
* @param key
* @param values
* @param context
* @throws IOException
* @throws InterruptedException
*
* // TODO: 2021/10/7 当map的所有输出数据来到reduce之后 该如何使用reduce方法进行处理呢?
* 1.排序 规则 根据key的字典进行排序 a-z
* 2.分组 规则 key相同的分为一组
* 3.分组之后,同一组的数据组成新的ke键值对,调用一次reduce方法 reduce方法基于调用的 一个分组调用一次
* // TODO: 2021/10/7 同一组数据组成一个新的ke键值对
* 新key:改组件共同的key
* 新value :该组所有的value组成一个新的迭代器Iterable
*/
private LongWritable outValue = new LongWritable();
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
//统计变量
long count = 0;
for (LongWritable value : values) {
//累加计算总次数
count += value.get();
}
outValue.set(count);
//最终使用上下文输出
context.write(key,outValue);
}
}
编写驱动类 注意,里面的args要记得设置他的输入输去路径 这里我直接在IDEA里面设置了
package com.zhan.mapreduce.wordcount;
/**
* @Classname WordCountDriver
* @Description MrZhan
* @Date 2021/10/7 19:40
* @Created by 1
*/
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
/**
* 该类就是MapReduce程序客户端驱动类 主要是构造Job对象实例
* 指定各种组件属性 包括 mapper reducer类 输入输出的数据类型、输入输出的输出路径
* 提胶job作业 job.submit()
*/
public class WordCountDriver_ve1 {
public static void main(String[] args) throws Exception {
//创建配置对象
Configuration conf = new Configuration();
// conf.set("mapreduce.framework.name","yarn");
//构造Job作业实例 参数 (配置对象,Job名字)
Job job = Job.getInstance(conf, WordCountDriver_ve1.class.getSimpleName());
//设置mr程序运行的主类
job.setJarByClass(WordCountDriver_ve1.class);
//设置本次mr程序的mapper类型 reduce类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
//指定mapper阶段输出的key value数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//指定reduce阶段输出的key value类型 也是mr程序最终的输出数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//todo 修改reducetask个数
job.setNumReduceTasks(1);
//todo 设置MapReduce 程序 Combiner 慎重使用
job.setCombinerClass(WordCountReducer.class);
//配置本次作业的输入输出路径 和输出输出路径
// TODO: 2021/10/7 TextInputFormat TextOutputFormat
Path input = new Path(args[0]);
Path output = new Path(args[1]);
// 配置作业的输入数据路径
FileInputFormat.setInputPaths(job,input);
// 配置作业的输出数据路径
FileOutputFormat.setOutputPath(job,output);
TODO: 2021/10/8 判断输出路径是否存在,如果存在就删除
FileSystem fs = FileSystem.get(conf);
if(fs.exists(output)){
fs.delete(output,true);//rm -fr
}
//最终提交本次作业
//采用waitForCompletion提交job 参数表示是否开启试试监控追踪作业的执行情况
boolean resuultflag = job.waitForCompletion(true);
//退出程序 和job结构进行绑定
System.exit(resuultflag ? 0:1);
}
}
新版的驱动类写法,推荐使用
package com.zhan.mapreduce.wordcount;
/**
* @Classname WordCountDriver_ve2
* @Description MrZhan
* @Date 2021/10/7 23:16
* @Created by 1
*/
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
/**
* 使用工具类TooRunner提交MapReduce作业
*/
public class WordCountDriver_ve2 extends Configured implements Tool {
public static void main(String[] args) throws Exception{
//创建配置对象
Configuration conf = new Configuration();
TODO: 2021/10/8 使用工具类ToolRunner提交任务
int status = ToolRunner.run(conf, new WordCountDriver_ve2(), args);
//退出客户端
System.out.println(status);
}
@Override
public int run(String[] args) throws Exception {
//构造Job作业实例 参数 (配置对象,Job名字)
Job job = Job.getInstance(getConf(), WordCountDriver_ve2.class.getSimpleName());
//设置mr程序运行的主类
job.setJarByClass(WordCountDriver_ve1.class);
//设置本次mr程序的mapper类型 reduce类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
//指定mapper阶段输出的key value数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//指定reduce阶段输出的key value类型 也是mr程序最终的输出数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//配置本次作业的输入输出路径 和输出输出路径
// TODO: 2021/10/7 TextInputFormat TextOutputFormat
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//采用waitForCompletion提交job 参数表示是否开启试试监控追踪作业的执行情况
return job.waitForCompletion(true)? 0:1;
}
}