设计思路:
数据部分截取,用于方便分析
---------------------------------
empno ename mgr sal
7369 SMITH 7902 800
7499 ALLEN 7698 1600
7521 WARD 7698 1250
7566 JONES 7839 2975
7654 MARTIN 7698 1250
7698 BLAKE 7839 2850
7782 CLARK 7839 2450
7839 KING 5000
7844 TURNER 7698 1500
7900 JAMES 7698 950
7902 FORD 7566 3000
-----------------------------------
map阶段
读取:7566 JONES 7839 2975
输出:在输出的value中用一个状态位标示该人员身份,用emp表示员工;mgr表示管理者
7566 emp,JONES,2975 --表示7566是一个员工,员工姓名为JONES,薪水为2975
7839 mgr,JONES,2975 --表示7839是一个管理者,他的下属是JONES,该下属的薪水为2975
以下类似的
读取:7698 BLAKE 7839 2850
输出:
7698 emp,BLAKE,2850
7839 mgr,BLAKE,2850
读取:7782 CLARK 7839 2450
输出:
7782 emp,CLARK,2450
7839 mgr,CLARK,2450
读取:7839 KING 5000
输出:
7839 emp,KING,5000
null mgr,KING,5000 (这类数据直接筛去,不write)
----------------------------------
reduce阶段
读取:
key为7839的数据聚集
7839 mgr,JONES,2975
7839 mgr,BLAKE,2850
7839 mgr,CLARK,2450
7839 emp,KING,5000
这里就汇总了员工号为7839的的数据集合,根据值中的关键标示emp和mgr来做数据处理:
值为emp时,7839作为员工,值value里面的是他的姓名和薪水;
值为mgr时,7839作为管理者,值value里面是他的下属员工和下属员工的薪水。
有了员工及其管理者的姓名和薪水,那么接下来的处理就简单了。
package week06;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
/**
* 5) 列出工资比上司高的员工姓名及其工资
* */
public class Emp_Test5 extends Configured implements Tool {
/**
* 计数器 用于计数各种异常数据
*/
enum Counter {
LINESKIP,
}
/**
* MAP任务
*/
public static class Map extends Mapper {
@Override
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();// 每行文件
// 输入文件首行,不处理
if (line.contains("empno") == true) {
return;
}
Employee emp = Employee.parser(line);
if (emp.isValid()) {
context.write(new Text(emp.getEmpno()),
new Text("emp," + emp.getEname() + "," + emp.getSal()));
if (!"".equals(emp.getMgr())) {//管理者字段不为空时
context.write(new Text(emp.getMgr()),
new Text("mgr," + emp.getEname() + "," + emp.getSal()));
}
} else {
context.getCounter(Counter.LINESKIP).increment(1); // 出错令计数器+1
return;
}
}
}
/**
* REDUCE
*/
public static class Reduce extends Reducer {
@Override
public void reduce(Text key, Iterable values, Context context)
throws IOException, InterruptedException {
List empList = new ArrayList();
int mgrSal=0;
//遍历reduce的输入的Iterable集合,将所有人员都加入到人员列表数组中
for (Text emp : values) {
empList.add(new Text(emp.toString()));
String[] tmps = emp.toString().split(",");
if (tmps.length == 3) {
//遍历的过程中,取得管理者的薪水,注意:管理者本人在reduce输入的Iterable集合中是标志为emp,即作为一个员工
if (tmps[0].equals("emp")) {
mgrSal = new Integer(tmps[2]);
}
}
}
//System.out.println("管理者:"+key+"--管理者薪资:" + mgrSal);
//遍历人员列表数组
for (Text emp : empList) {
String[] tmps = emp.toString().split(",");
if (tmps.length == 3) {
//System.out.println(tmps[1]+"--下属薪资:" + tmps[2]);
//将员工取出来,并比较其薪水是否比其管理者的薪水高,高则输出
//注意:该管理者的下属在reduce输入的Iterable集合中是标志为mgr
if (tmps[0].equals("mgr")
&& (Integer.parseInt(tmps[2]) - mgrSal > 0)) {
System.out.println("bingo!!!!!");
context.write(new Text(tmps[1]), new Text(tmps[2]));
}
}
}
}
}
public int run(String[] args) throws Exception {
Configuration conf = getConf();
conf.set("mapred.job.tracker", "192.168.1.201:9001");
String[] ioArgs = new String[] { "emp_in", "emp_out_test5" };
String[] otherArgs = new GenericOptionsParser(conf, ioArgs)
.getRemainingArgs();
if (otherArgs.length != 2) {
System.err.println("Usage: Test < input path > < output path >");
System.exit(2);
}
Job job = new Job(conf, "week06_test_05"); // 任务名
job.setJarByClass(Emp_Test5.class); // 指定Class
FileInputFormat.addInputPath(job, new Path(otherArgs[0])); // 输入路径
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1])); // 输出路径
job.setMapperClass(Map.class); // 调用上面Map类作为Map任务代码
job.setReducerClass(Reduce.class);// 调用上面Reduce类作为Reduce任务代码
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class); // 指定输出的KEY的格式
job.setOutputValueClass(Text.class); // 指定输出的VALUE的格式
job.waitForCompletion(true);
// 输出任务完成情况
System.out.println("任务名称:" + job.getJobName());
System.out.println("任务成功:" + (job.isSuccessful() ? "是" : "否"));
System.out.println("输入行数:"
+ job.getCounters()
.findCounter("org.apache.hadoop.mapred.Task$Counter",
"MAP_INPUT_RECORDS").getValue());
System.out.println("输出行数:"
+ job.getCounters()
.findCounter("org.apache.hadoop.mapred.Task$Counter",
"MAP_OUTPUT_RECORDS").getValue());
System.out.println("跳过的行:"
+ job.getCounters().findCounter(Counter.LINESKIP).getValue());
return job.isSuccessful() ? 0 : 1;
}
/**
* 设置系统说明 设置MapReduce任务
*/
public static void main(String[] args) throws Exception {
// 记录开始时间
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date start = new Date();
// 运行任务
int res = ToolRunner.run(new Configuration(), new Emp_Test5(), args);
// 输出任务耗时
Date end = new Date();
float time = (float) ((end.getTime() - start.getTime()) / 60000.0);
System.out.println("任务开始:" + formatter.format(start));
System.out.println("任务结束:" + formatter.format(end));
System.out.println("任务耗时:" + String.valueOf(time) + " 分钟");
System.exit(res);
}
}