MapReduce编程规范
用户编写的程序分为3个部分:Mapper、Reducer、Driver(提交mr程序的客户端)
Mapper部分
1. 自定义类,继承Mapper类型
2. 定义K1,V1,K2,V2的泛型(K1,V1是Mapper的输入数据类型,K2,V2是Mapper的输出数据类型)
3. 重写map方法(处理逻辑)
参考下图:
注意:map方法,每一个KV对都会调用一次。
Reducer部分
1. 自定义类,继承Reducer类型
2. 定义K2,V2,K3,V3的泛型(K2,V2是Reducer的输入数据类型,K3,V3是Reducer的输出数据类型)
3. 重写reduce方法(处理逻辑)
参考下图:
注意: reduce方法,默认按key分组,每一组都调用一次。
Driver部分
整个程序需要一个Driver来进行提交,提交的是一个描述了各种必要信息的job对象,如下
1. 获取Job对象
2. 指定驱动类
3. 设置Mapper和Reducer类型
4. 设置Mapper的输出K2、V2的类型(如果类型和K3,V3相同,可省略)
5. 设置Reducer的输出K3、V3的类型
6. 设置Reduce的个数(默认为1)
7. 设置Mapper的输入数据的路径
8. 设置Reducer的输出数据的路径
9. 提交作业
参考下图:
注:
一个完整mapreduce必须要有mapper,但是可以没有reducer。
key-vlue之间的默认分隔符是tab键。
reduce阶段输出key是根据对应的类型排序。
案例:
输入如下数据:(日期 类别 销量 销售金额)
2021-7-4 camer 1 5225.5
2021-7-4 camer 6 86222.52
2021-7-4 phone 2 3568.66
2021-7-4 phone 3 32312.00
2021-7-5 camer 1 3424.3
2021-7-5 camer 3 34223.6
2021-7-5 camer 2 8768.88
2021-7-5 phone 1 2322.35
2021-7-5 phone 5 45732.00
根据如上数据,统计每天、每个类别的总销售量和总销售金额。输出类似如下:
2021-7-4 camer 7 5225.5+86222.52(加和值)
2021-7-4 phone 5 3568.66+32312.00(加和值)
2021-7-5 camer 6 3424.3+34223.6+8768.88(加和值)
2021-7-5 phone 6 2322.35+45732.00(加和值)
配置pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ali</groupId>
<artifactId>qf-2102</artifactId>
<version>1.0</version>
<!--定义版本常量-->
<properties>
<hadoop.version>2.7.6</hadoop.version>
</properties>
<!--引入依赖-->
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
</project>
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.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.output.FileOutputFormat;
import java.io.IOException;
public class MyWordCount {
public static class MyMapper extends Mapper<LongWritable, Text, Text, Text> {
public Text k = new Text();
public Text v = new Text();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//map阶段的核心业务逻辑
String line = value.toString();
String[] fileds = line.split(" ");
k.set(fileds[0]+" "+fileds[1]);
v.set(fileds[2]+" "+fileds[3]);
//输出
context.write(k, v);
}
}
public static class MyReducer extends Reducer<Text,Text, Text, Text> {
public Text v = new Text();
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
//定义计数器
int order_cnt=0;
double order_prices=0.00;
//循环每个key对一个的迭代器
for (Text t : values) {
String[] fields =t.toString().split(" ");
order_cnt +=Integer.valueOf(fields[0]);
order_prices +=Double.valueOf(fields[1]);
//给v赋值
v.set(order_cnt+" "+order_prices);
}
context.write(key,v);
}
}
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
//获取conf对象
Configuration conf = new Configuration();
//获取job实例
Job job = Job.getInstance(conf);
//为job设置运行的主类
job.setJarByClass(MyWordCount.class);
//设置map阶段参数
job.setMapperClass(MyMapper.class);
//设置mapper输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//设置reduce运行类
job.setReducerClass(MyReducer.class);
//设置reduce的输出参数类型(当reduce和map阶段输出数据类型一摸一样,reduce阶段可以省略)
job.setOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//设置map阶段输入数据路径
FileInputFormat.addInputPath(job, new Path(args[0]));
//设置reduce姐u但输出数据路径
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//job提交 true:提交并打印信息
boolean isok = job.waitForCompletion(true);
//程序退出
System.exit(isok ? 0 : 1);
}
}