Map 阶段 2 个步骤
- 设置 InputFormat 类, 将数据切分为 Key-Value(K1和V1) 对, 输入到第二步
- 自定义 Map 逻辑, 将第一步的结果转换成另外的 Key-Value(K2和V2) 对, 输出结果
Shuffle 阶段 4 个步骤
- 对输出的 Key-Value 对进行分区
- 对不同分区的数据按照相同的 Key 排序
- (可选) 对分组过的数据初步规约, 降低数据的网络拷贝
- 对数据进行分组, 相同 Key 的 Value 放入一个集合中
Reduce 阶段 2 个步骤
- 对多个 Map 任务的结果进行排序以及合并, 编写 Reduce 函数实现自己的逻辑, 对输入的 Key-Value 进行处理, 转为新的 Key-Value(K3和V3)输出
- 设置 OutputFormat 处理并保存 Reduce 输出的 Key-Value 数据
计数统计:
继承Mapper类
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/*继承Mapper类
* */
public class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//将传入的k1拆分成一个字符串数组
String[] split = value.toString().split(",");
2:遍历数组,组装 K2(Stiring 为Text) 和 V2(1),写入到上下文中
for (String s : split) {
context.write(new Text(s),new LongWritable(1));
}
}
}
继承Reducer类
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/*继承Reduce类
* 老的 KEYIN: K2类型
VALULEIN: V2类型
KEYOUT: K3类型
VALUEOUT:V3类型
* */
public class MyReduce extends Reducer<Text,LongWritable,Text,LongWritable> {
/* 参数:
key : 新K2
values: 集合 新 V2 <1,1,1,1>
context :表示上下文对象*/
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
//将k2变成K3
long count =0;
for (LongWritable value : values) {
count += value.get();
}
//将新的组装的k3 v3赋值给上下文
context.write(key,new LongWritable(count));
}
}
定义主类, 描述 Job 并提交 Job
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
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.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.net.URI;
/*
* MapReduce练习
* 定义主类, 描述 Job 并提交 Job
* */
public class Mytest extends Configured implements Tool {
//该方法用于指定一个Job任务
@Override
public int run(String[] strings) throws Exception {
//创建一个Job任务对象
Job job = Job.getInstance(super.getConf(), Mytest.class.getName());
//配置job任务对象(8个步骤)
//第一步:指定文件的读取方式和读取路径
job.setInputFormatClass(TextInputFormat.class);
TextInputFormat.addInputPath(job,new Path("hdfs://node01:8020/mulu/wordcount.txt"));
//第二步:指定Map阶段的处理方式和数据类型
job.setMapperClass(MyMapper.class);
//设置Map阶段K2的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//第3到6步采用默认
//第七步指定reduce阶段的处理方式和数据类型
job.setReducerClass(MyReduce.class);
//设置K3V3的输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//第8步设置输出路径和类型
job.setOutputFormatClass(TextOutputFormat.class);
//路径
Path path = new Path("hdfs://node01:8020/MY");
TextOutputFormat.setOutputPath(job,path);
//获取FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020/MY"), new Configuration());
boolean b = fileSystem.exists(path);
if(b){
fileSystem.delete(path,true);
}
//等待任务接收
boolean b1 = job.waitForCompletion(true);
return b1?0:1;
}
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
//启动任务
int run = ToolRunner.run(configuration, new Mytest(), args);
System.exit(run);
}
}
分区
继承Mapper类
/*定义一个mapper ,不做处理,只接收数据*/
public class MyMapperone extends Mapper<LongWritable,Text,Text,NullWritable>{
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
Counter counter = context.getCounter("MY_LEIXI", "MY_COUNT");
counter.increment(1L);
//key的值和k1的value一样,value 为空
context.write(value,NullWritable.get());
}
继承Partitioner类(分区)
/*进行分区*/
public class MyPartitioner extends Partitioner<Text,NullWritable>{
@Override
public int getPartition(Text text, NullWritable nullWritable, int i) {
//获取需要的数据,索引为5的数据
String s = text.toString().split("\t")[5];
if(Integer.parseInt(s) >15){
return 1;
}
return 0;
}
继承Reducer类
/*Reducer ,分区之后的数据为索引第5的数据,不需要进行转换可以直接输出
* */
public class MyReducer extends Reducer<Text,NullWritable,Text,NullWritable> {
@Override
protected void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
context.write(key,NullWritable.get());
}
定义主类, 描述 Job 并提交 Job
//分区后job定义和操作
public class FenQu extends Configured implements Tool{
@Override
public int run(String[] strings) throws Exception {
//创建job对象
Job job = Job.getInstance(super.getConf(), FenQu.class.getName());
//对JOB进行操作
//第一步:设置输入类型和输入的路径
job.setInputFormatClass(TextInputFormat.class);
TextInputFormat.setInputPaths(job,new Path("hdfs://node01:8020/mulu/partition.csv"));
//第二步:设置mapper类和输出的数据类型
job.setMapperClass(MyMapperone.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(NullWritable.class);
//第三部(指定分区)
job.setPartitionerClass(MyPartitioner.class);
//第4.5.6 省略
//第7步指定输出的Reducer类和数据类型
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class);
//设置分区的ReduceTasks个数
job.setNumReduceTasks(2);
//第8步:指定输出类和路径
job.setOutputFormatClass(TextOutputFormat.class);
TextOutputFormat.setOutputPath(job,new Path("hdfs://node01:8020/MY2"));
boolean b = job.waitForCompletion(true);
return b?0:1;
}
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
//启动任务
int run = ToolRunner.run(configuration, new FenQu(), args);
//退出
System.exit(run);
}
}
集群运行模式
- 将 MapReduce 程序提交给 Yarn 集群, 分发到很多的节点上并发执行
- 处理的数据和输出结果应该位于 HDFS 文件系统
- 提交集群的实现步骤: 将程序打成JAR包,并上传,然后在集群上用hadoop命令启动
hadoop jar 打包上传的文件路径 主方法的路径
本地运行模式
- MapReduce 程序是在本地以单进程的形式运行
- 处理的数据及输出结果在本地文件系统(输入输出的路径改成本地即可)