原理
yarn的调度过程看下面这篇博客
https://blog.csdn.net/qq_45335413/article/details/108186427
MapReduce的编程模型,看下面这篇博客
https://blog.csdn.net/qq_45335413/article/details/107722220
一、开发自己的WordCount程序
1、导入下面的jar包
$HADOOP_HOME/share/hadoop/common/*.jar
$HADOOP_HOME/share/hadoop/common/lib/*.jar
$HADOOP_HOME/share/hadoop/mapreduce/*.jar
$HADOOP_HOME/share/hadoop/mapreduce/lib/*.jar
2、分析分析WordCount数据处理的过程
3、编写代码三个类
3.1 WorldCountMap
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* @author 吴明辉
* @date 2020/9/2-${TIEM}
*/
public class WorldCountMap extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
protected void map(LongWritable key1, Text value1, Context context) throws IOException, InterruptedException {
/*
* context代表Map的上下文
* 上文:HDFS
* 下文:Reducer
*/
//拿到一条数据
String data = value1.toString();
//对数据进行分词操作,得到words数组
String[] worlds = data.split(" ");
//对words数组进行遍历,输出到reduce
for (String world : worlds) {
context.write(new Text(world),new IntWritable(1));
}
}
}
3.2 WorldCountReduce
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.io.Text;
import java.io.IOException;
/**
* @author 吴明辉
* @date 2020/9/2-${TIEM}
*/
public class WorldCountReduce extends Reducer<Text, IntWritable,Text,IntWritable> {
@Override
protected void reduce(Text key3, Iterable<IntWritable> values3, Context context) throws IOException, InterruptedException {
//遍历数组的集合元素做一个累加
int sum = 0;
for (IntWritable i:
values3) {
sum = sum + i.get();
}
//输出结果
context.write(key3,new IntWritable(sum));
}
}
3.3WorldCountMain
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.fs.Path;
/**
* @author 吴明辉
* @date 2020/9/2-${TIEM}
*/
public class WorldCountMain {
public static void main(String[] args) throws Exception {
// 1、创建任务Job,并且指定任务的入口
Job job = Job.getInstance(new Configuration());
job.setJarByClass(WorldCountMain.class);
// 2、指定任务的Map、和Map的输出类型<k2 v2>
job.setMapperClass(WorldCountMap.class);
job.setMapOutputKeyClass(Text.class); // k2 单词
job.setMapOutputValueClass(IntWritable.class); //v2 记一次数
// 3、指定任务的Reduce、和Reduce输出的类型<k4 v4>
job.setReducerClass(WorldCountReduce.class);
job.setOutputKeyClass(Text.class); // k4
job.setOutputValueClass(IntWritable.class); // v4
// 4、指定任务的输入路径、和输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 5、执行任务
job.waitForCompletion(true);
}
}
注意事项:
1、输出路径必须不存在
2、虚拟内存给关闭
3、导入类不能导错
二、高级特性
1、序列化Writable
回顾:Java的序列化例子
https://blog.csdn.net/qq_45335413/article/details/108390866
同样道理,Hadoop序列化需要需要继承Writable接口。
思考:yarn在执行mapper和reduce的时候,是不是要进行6次的io操作,对象在io操作保存数据的时候,是不是要进行序列的操作,Hadoop中已经把所有的数据类型做了一个序列化,只需要继承Writable接口就行了 。
1.1 Employee类
public class Employee implements Writable{
private int empno;
private String ename;
private String job;
private int mgr;
private String hiredate;
private int sal;
private int comm;
private int deptno;
@Override
public String toString() {
return "Employee [empno=" + empno + ", ename=" + ename + ", sal=" + sal + ", deptno=" + deptno + "]";
}
@Override
public void readFields(DataInput input) throws IOException {
// 反序列化:输入
this.empno = input.readInt();
this.ename = input.readUTF();
this.job = input.readUTF();
this.mgr = input.readInt();
this.hiredate = input.readUTF();
this.sal = input.readInt();
this.comm = input.readInt();
this.deptno = input.readInt();
}
//注意:序列化的顺序一定要和反序列化的顺序一致!!!!!
@Override
public void write(DataOutput output) throws IOException {
// 序列化:输出
output.writeInt(this.empno);
output.writeUTF(this.ename);
output.writeUTF(this.job);
output.writeInt(this.mgr);
output.writeUTF(this.hiredate);
output.writeInt(this.sal);
output.writeInt(this.comm);
output.writeInt(this.deptno);
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public String getHiredate() {
return hiredate;
}
public void setHiredate(String hiredate) {
this.hiredate = hiredate;
}
public int getSal() {
return sal;
}
public void setSal(int sal) {
this.sal = sal;
}
public int getComm() {
return comm;
}
public void setComm(int comm) {
this.comm = comm;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
}
1.2 EmployeeMapper
// k1偏移量 v1文本 k2 员工号 v2员工对象
public class EmployeeMapper extends Mapper<LongWritable, Text, IntWritable, Employee> {
@Override
protected void map(LongWritable key1, Text value1, Context context)
throws IOException, InterruptedException {
//7654,MARTIN,SALESMAN,7698,1981/9/28,1250,1400,30
String data = value1.toString();
//分词
String[] words = data.split(",");
//创建员工对象
Employee e = new Employee();
e.setEmpno(Integer.parseInt(words[0])); //员工号
e.setEname(words[1]); //员工号
e.setJob(words[2]); //职位
e.setMgr(Integer.parseInt(words[3]));//老板号
e.setHiredate(words[4]);//入职日期
e.setSal(Integer.parseInt(words[5]));//月薪
e.setComm(Integer.parseInt(words[6])); //奖金
e.setDeptno(Integer.parseInt(words[7]));
//输出 k2员工号 v2员工对象
context.write(new IntWritable(e.getEmpno()), e);
}
}
1.3 EmployeeMain
public class EmployeeMain {
public static void main(String[] args) throws Exception {
// 1、创建任务Job,并且指定任务的入口
Job job = Job.getInstance(new Configuration());
job.setJarByClass(EmployeeMain.class);
// 2、指定任务的Map、和Map的输出类型<k2 v2>
job.setMapperClass(EmployeeMapper.class);
job.setMapOutputKeyClass(IntWritable.class); // k2
job.setMapOutputValueClass(Employee.class); //v2
// 3、指定任务的Reduce、和Reduce输出的类型<k4 v4>
job.setOutputKeyClass(IntWritable.class); // k4
job.setOutputValueClass(Employee.class); // v4
// 4、指定任务的输入路径、和输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 5、执行任务
job.waitForCompletion(true);
}
}
2、排序
Hadoop是默认进行数字升序的排序,字母大写在前小写在后,按字典顺序进行排序。
Hadoop是按照Map输出的key2进行排序
k2可以是
(1)基本的数据类型:数字:升序
字符串:字典顺序
实现自定义的排序:继承 ****.Comparator
(2)对象
(*)一个列的排序:按照员工的薪水排序
(*)多个列的排序:先按照部门号排序,再按照薪水
select *
from emp
order by deptno,sal;
接口:WritableComparable
2.1 实现int类型的自排序
//实现Int类型的自定义排序
public class MyIntWritableComparator extends IntWritable.Comparator {
@Override
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
// TODO Auto-generated method stub
return -super.compare(b1, s1, l1, b2, s2, l2);
}
}
然后再main函数里添加
//加入自定义的排序规则
job.setSortComparatorClass(MyIntWritableComparator.class);
2.2 实现text类型的自排序
同上
2.3 实现对象的自排序
只展示核心代码
public class Employee implements WritableComparable<Employee>{
private int empno;
private String ename;
private String job;
private int mgr;
private String hiredate;
private int sal;
private int comm;
private int deptno;
// @Override
// public int compareTo(Employee e) {
// // 一个列的实现排序规则:按照员工的薪水排序
// if(this.sal >= e.getSal()) {
// return 1;
// }else {
// return -1;
// }
// }
@Override
public int compareTo(Employee e) {
//实现多个列的排序:先按照部门号排序,再按照薪水
if(this.deptno > e.getDeptno()) {
return 1;
}else if(this.deptno < e.getDeptno()) {
return -1;
}
//等号放到最后
if(this.sal >= e.getSal()) {
return 1;
}else {
return -1;
}
}
}
3、分区Partition
MapReduce默认只有一个分区(输出文件),其根据Map的输出来建立分区。
3.1 什么是分区?
在这里拿sql执行计划进行举例,当sql进行查询的操作的时候,例如查询1500至2000之间的员工工资,是不是对整张表进行查询,这样的效率是不是比较低。
我们可以进行优化:
将整张表进行分区,分成三块,如上图,我们还是查询1500至2000之间的员工工资,这样我们只需要在分区1进行查询就可以,这样是不是提高了查询的效率
大家可以重点百度一下hash分区的思想,在这里就不多说了。
3.2 Java代码实现MapReduce分区
举例实现:将不同的部门好,分别存储在不同的文件里
1、创建MyEmployeePartitioner 类
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Partitioner;
//实现分区规则
//根据Map的输出<k2 v2>来建立分区 k2 部门号 v2 员工对象
public class MyEmployeePartitioner extends Partitioner<IntWritable, Employee> {
@Override
public int getPartition(IntWritable k2, Employee v2, int numTask) {
// numTask:分区的个数
//得到部门号
int deptno = v2.getDeptno();
if(deptno == 10) {
//放入1号分区
return 1%numTask;
}else if(deptno == 20) {
//放入2号分区
return 2%numTask;
}else {
//放入0号分区
return 3%numTask;
}
}
}
2、添加到main函数里,进行启动加载
//加入分区规则,几个分区
job.setPartitionerClass(MyEmployeePartitioner.class);
job.setNumReduceTasks(3); //分区的个数
4、combiner合并
4.1 什么是combiner
总之,combiner是一种特殊的Reducer,在Map端,先执行一次合并;用于减少输出到Reducer的数据量,减少网络的开销,提高性能,是MapReduce的一种优化
注意: 并不是所有的情况都能使用Combiner(eg:求平均)
wrong value class: class org.apache.hadoop.io.DoubleWritable
is not class org.apache.hadoop.io.IntWritable
引入Combiner以后,不能改变原理的逻辑,特别输出的数据类型搞清楚
4.2 Java代码实现
只需要在main函数启动类中,添加
//加入一个Combiner的class
job.setCombinerClass(reduce类.class);
三、MapReduce核心:Shuffle
shuffle也就是MapReduce的资源调度的过程,首先数据块读入程序之前,先转化为数据切片,然后,map再一条一条数据处理,数据处理之后,会放到一个环形缓冲区里,环形缓冲区达到80%之后,会触发一个检查点,数据发生溢写,剩余的20%将存放新的数据。发生溢写时,会进行两个操作,一个分区,一个排序。之后的数据才送到reduce,reduce的数据程序执行之前也会发生像map的shuffle操作,先从每个map拉取数据,由于是不同的map数据,因此先进行merge操作,再进行排序。
- 为什么要进行排序?
Reduce端需要对数据进行分组,将key相同的放在一起规约。为达到目的,有两种算法:hashmap和sort,前者太耗内存,而排序可以通过外排对任意数据量分组,只要磁盘数据量够大就行。map端排序是为了减轻reduce端排序的压力。
本质就是,map前后都有一个缓冲区,reduce前后也都有一个缓冲区。在缓冲区里有执行一些其他的操作。
四、MapReduce编程案例
1、实现distinct
去重:利用 k2和k3的关系,k2到k3的时候,有一个去重和分组的功能!
1.1 编写distinctMapper
public class distinctMapper extends Mapper<LongWritable, Text,Text, NullWritable> {
@Override
protected void map(LongWritable key1, Text value1, Context context) throws IOException, InterruptedException {
//取出数据
String data = value1.toString();
//分词
String[] world = data.split(",");
//输出
context.write(new Text(world[2]),NullWritable.get());
}
}
1.2 编写distinctReduce
public class distinctReduce extends Reducer<Text , NullWritable,Text,NullWritable> {
@Override
protected void reduce(Text key3, Iterable<NullWritable> values3, Context context) throws IOException, InterruptedException {
//直接输出数据
context.write(key3,NullWritable.get());
}
}
1.3 distinctMain
拷贝上面主函数代码
2、多表查询
2.1map join:适用于有一张小表。输出部门名称、员工姓名
什么是mapjoin?
当一个表的数据较少时,可以在初始化的时候预先存入内存中,缓存小表,这样可以提高效率。注意mapjoin是每个map阶段都要加载setup方法进行缓存数据。
下面的代码举例:emp.csv、dept.csv表,输出部门名称、员工姓名进行举例
代码如下:
注意setup的代码。
(1)编写mapjoinMapper 类
public class mapjoinMapper extends Mapper<LongWritable, Text,Text,Text> {
Map deptData = new HashMap();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//7934,MILLER,CLERK,7782,1982/1/23,1300,0,10
//拿数据进行分词
String data = value.toString();
String[] fields = data.split(",");
String deptno = (String) deptData.get(fields[7]);
//输出数据
context.write(new Text(deptno) , new Text(fields[1]));
//
//
}
@Override
protected void setup(Context context) throws IOException, InterruptedException {
//1、拿到路径
String path = context.getCacheFiles()[0].getPath();
//2、拿到缓存文件名
int index = path.lastIndexOf("/");
String deptName = path.substring(index + 1);
//3、按行读数据,并存入map集合中
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(deptName)));
String s;
while ((s=bufferedReader.readLine()) != null ){
String[] fields = s.split(",");
//40,OPERATIONS,BOSTON
deptData.put(fields[0],fields[1]);
}
}
}
(2)编写mapjoinMain
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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 mapjoinMain {
public static void main(String[] args) throws Exception {
// 创建一个任务
Job job = Job.getInstance(new Configuration());
job.setJarByClass(mapjoinMain.class);
job.setMapperClass(mapjoinMapper.class);
job.setMapOutputKeyClass(Text.class); //k2 部门名称
job.setMapOutputValueClass(Text.class); //v2 员工姓名
//启动任务的时候,就将部门表进行缓存
job.addCacheFile(new URI(args[0])); //第一参数代表部门表: /scott/dept.csv
FileInputFormat.setInputPaths(job, new Path(args[1])); //第二张表:员工表
FileOutputFormat.setOutputPath(job, new Path(args[2])); //输出的路径
// 5、执行任务
job.waitForCompletion(true);
}
}
2.2reduce join:适用于都是大表的情况
上图为reduce join的数据处理过程。
sql语句的写法:
查询员工信息:部门名称、员工名字
select d.dname,e.ename
from emp e,dept d
where e.deptno=d.deptno;
Java代码实现:
(1)ReduceJoinMapper 类
public class ReduceJoinMapper extends Mapper<LongWritable, Text, IntWritable,Text> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1、拿数据进行分词
String data = value.toString();
String[] fields = data.split(",");
//2、判断是emp还是dept文件
if (fields.length == 3){
//3、假如是dept文件,输出部门号,*部门名称
context.write(new IntWritable(Integer.parseInt(fields[0])),new Text("*"+fields[1]));
}else {
//4、假如emp文件,输出部门号,员工名称
context.write(new IntWritable(Integer.parseInt(fields[7])),new Text(fields[1]));
}
}
}
(2)ReduceJoinReducer 类
public class ReduceJoinReducer extends Reducer<IntWritable, Text,Text,Text> {
@Override
protected void reduce(IntWritable key3, Iterable<Text> values3, Context context) throws IOException, InterruptedException {
String dept = ""; // 不能string dept = null 会报空指针异常!
String empList = "";
String temp;
//1、遍历values3集合
for (Text v : values3) {
//2、判断出是部门号,还是姓名
temp = v.toString();
if (temp.startsWith("*")){
dept = temp.substring(1);
}else {
empList = empList + "," + temp;
}
}
//3、部门号作为k4进行输出,姓名进行累加,以字符串形式作为v4尽心输出
context.write(new Text(dept) , new Text(empList) );
}
}
(3)ReduceJoinMain 类
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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 ReduceJoinMain {
public static void main(String[] args) throws Exception {
// 1、创建任务Job,并且指定任务的入口
Job job = Job.getInstance(new Configuration());
job.setJarByClass(ReduceJoinMain.class);
// 2、指定任务的Map、和Map的输出类型<k2 v2>
job.setMapperClass(ReduceJoinMapper.class);
job.setMapOutputKeyClass(IntWritable.class); // k2 部门号
job.setMapOutputValueClass(Text.class); //v2
// 3、指定任务的Reduce、和Reduce输出的类型<k4 v4>
job.setReducerClass(ReduceJoinReducer.class);
job.setOutputKeyClass(Text.class); // k4
job.setOutputValueClass(Text.class); // v4
// 4、指定任务的输入路径、和输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 5、执行任务
job.waitForCompletion(true);
}
}
3、链式处理:多个map、多个reduce
举例:编写MapReduce程序实现下面的需求。
需求一:根据员工的职位涨工资。总裁:1000;经理:800;其他人员:400
方式一:case ... when .... (标准的SQL)
select empno,ename,job,sal 涨前,
case job when 'PRESIDENT' then sal+1000
when 'MANAGER' then sal + 800
else sal+ 400
end 涨后
from emp;
方式二:decode函数(是Oracle的函数)
select empno,ename,job,sal 涨前,
decode(job,'PRESIDENT',sal+1000,
'MANAGER',sal+800,
sal+400) 涨后
from emp;
需求二:根据部门号涨工资。10号部门:1000;20号部门:800;其他部门:400
需求三:求部门的工资总额。
需求四:求部门工资总额大于15000的部门。
java代码实现如下:
(1)、创建Employee类,get与set方法自己生成
public class Employee implements Writable {
private int empno;
private String ename;
private String job;
private int mgr;
private String hiredate;
private int sal;
private int comm;
private int deptno;
@Override
public void readFields(DataInput input) throws IOException {
// 反序列化:输入
this.empno = input.readInt();
this.ename = input.readUTF();
this.job = input.readUTF();
this.mgr = input.readInt();
this.hiredate = input.readUTF();
this.sal = input.readInt();
this.comm = input.readInt();
this.deptno = input.readInt();
}
@Override
public void write(DataOutput Output) throws IOException {
// 序列化输出
Output.writeInt(this.empno);
Output.writeUTF(this.ename);
Output.writeUTF(this.job);
Output.writeInt(this.mgr);
Output.writeUTF(this.hiredate);
Output.writeInt(this.sal);
Output.writeInt(this.comm);
Output.writeInt(this.deptno);
}
}
(2)、创建GetEmployeeMap,这个mapper的功能主要是把取得每条数据,封装在employee对象里面
public class GetEmployeeMap extends Mapper<LongWritable, Text, IntWritable,Employee> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
/*
* 1、读到一条数据
* 2、进行分词,得到一个数组
* 3、对整个employee 进行封装数据
* 4、进行输出
*
* */
String data = value.toString();
String[] words = data.split(",");
Employee e = new Employee();
e.setEmpno(Integer.parseInt(words[0])); //员工号
e.setEname(words[1]); //员工号
e.setJob(words[2]); //职位
e.setMgr(Integer.parseInt(words[3]));//老板号
e.setHiredate(words[4]);//入职日期
e.setSal(Integer.parseInt(words[5]));//月薪
e.setComm(Integer.parseInt(words[6])); //奖金
e.setDeptno(Integer.parseInt(words[7]));
context.write(new IntWritable(e.getEmpno()),e);
}
}
(3)、创建IncreaseSalaryByJobMap,主要实现根据员工职位涨工资。
public class IncreaseSalaryByJobMap extends Mapper<IntWritable,Employee,IntWritable,Employee> {
@Override
protected void map(IntWritable key, Employee emp, Context context) throws IOException, InterruptedException {
/*
* 1、读取员工职位
* 2、判断是什么职位
* 3、进行输出
*
* */
String job = emp.getJob();
if(job.equals("PRESIDENT")) {
emp.setSal(emp.getSal() + 1000); //总裁涨1000
}else if(job.equals("MANAGER")) {
emp.setSal(emp.getSal() + 800); //经理涨800
}else {
emp.setSal(emp.getSal() + 400);
}
context.write(new IntWritable(emp.getEmpno()),emp);
}
}
(4)、创建IncreaseSalaryByDeptMap , 主要实现根据部门号涨工资
public class IncreaseSalaryByDeptMap extends Mapper<IntWritable,Employee,IntWritable,Employee> {
@Override
protected void map(IntWritable empno, Employee emp, Context context) throws IOException, InterruptedException {
/*
*
* 1、取出部门号
* 2、进行判断
* 3、输出
* */
int deptno = emp.getDeptno();
if(deptno == 10) {
emp.setSal(emp.getSal() + 1000);
}else if(deptno == 20) {
emp.setSal(emp.getSal() + 800);
}else {
emp.setSal(emp.getSal() + 400);
}
context.write(new IntWritable(deptno),emp);
}
}
(5)、创建GetTotalSalaryBydeptnoReduce,主要实现部门的工资总额。在这块是reduce操作,也就是一个分组操作。
public class GetTotalSalaryBydeptnoReduce extends Reducer<IntWritable,Employee,IntWritable,IntWritable> {
@Override
protected void reduce(IntWritable deptno, Iterable<Employee> emp, Context context) throws IOException, InterruptedException {
int totalSalary =0;
for (Employee employee : emp) {
totalSalary = totalSalary + employee.getSal();
}
context.write(deptno , new IntWritable(totalSalary));
}
}
(6)、创建FilterTotalSalaryByDeptnoMap,主要筛选出工资大于15000的部门
public class FilterTotalSalaryByDeptnoMap extends Mapper<IntWritable, IntWritable, IntWritable, IntWritable> {
@Override
protected void map(IntWritable deptno, IntWritable total, Context context) throws IOException, InterruptedException {
if (total.get() > 15000){
context.write(deptno, total);
}
}
}
(7)、创建MyChainMain,注意导入的类,chainmapper与chainReduce导入的lib下的chain
import org.apache.hadoop.conf.Configuration;
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.chain.ChainMapper;
import org.apache.hadoop.mapreduce.lib.chain.ChainReducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.fs.Path;
/**
* @author 吴小辉
* @date 2020/9/16-${TIEM}
*/
public class MyChainMain {
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(MyChainMain.class);
//指定链式处理的每个Map和Reduce
ChainMapper.addMapper(job, GetEmployeeMap.class,
LongWritable.class, Text.class, IntWritable.class, Employee.class, conf);
ChainMapper.addMapper(job, IncreaseSalaryByJobMap.class,
IntWritable.class, Employee.class, IntWritable.class, Employee.class, conf);
ChainMapper.addMapper(job, IncreaseSalaryByDeptMap.class,
IntWritable.class, Employee.class, IntWritable.class, Employee.class, conf);
ChainReducer.setReducer(job, GetTotalSalaryBydeptnoReduce.class,
IntWritable.class, Employee.class, IntWritable.class, IntWritable.class, conf);
ChainReducer.addMapper(job, FilterTotalSalaryByDeptnoMap.class,
IntWritable.class, IntWritable.class, IntWritable.class, IntWritable.class, conf);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}
上面的代码提供的只是一个思想,如果读者没有数据的话,可以用自己的数据,只需要把Employee类进行根据自己的数据进行修改一下就行。