一、分布式计算MapReduce
--------------------------------------------------------
1.目的:取出历年来的最高气温(使用1901和1902数据源)
2.Map阶段
map(k,v)函数: 对输入数据的每一行进行map处理,需要自己编程实现,k--行号,v--数据
3.Reduce
reduce()函数:三个阶段
a.Shuffle --> 从每个map节点复制map输出数据,将数据进行重洗,然后分配
b.Sort --> 框架按key对input数据排序和整合(对相同的key的value进行合并),形成新的key values(合并后的values)
c.Reduce--> 对于key 和 values 进行目标提取,然后写出到outPath
4.MapReduce的作业流程:
map --> shuffle --> reduce
a.编写自定义map类 : MyMapper,重写map方法
b.编写自定义Reduce类: MyReducer
c.编写app程序,运行mr作业: MaxTemperature
二、Job
--------------------------------------------
1.允许用户配置作业,提交作业,查询状态,控制执行。
2.set 方法设置属性,只能在提交作业之前进行。
3.用户创建job之后,可以查看job的各方面信息
4.用户可以监控job过程
三、Windows 测试Job,异常解决
----------------------------------------------
java.lang.NullPointerExceptionjava.lang.ProcessBuilder.start(UnknownSource)atorg.apache.hadoop.util.Shell.runCommand(Shell.java:482)Exceptioninthread"main"java.lang.UnsatisfiedLinkError:org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z
解决方案:
解决方法是下载https://github.com/srccodes/hadoop-common-2.2.0-bin文件然后将其中的hadoop.dll文件放到hadoop安装路径的bin文件夹下(配置好HADOOP_HOME的环境变量),然后重启电脑,这样问题就能得到解决了!
如果还是不行,请检查本地的java安装目录是否有空格,比如progeam files ...,如果有,请尝试将jdk安装到无空格目录或者采用下面的方法
找到windows hadoop安装目录,E:\hadoop-2.7.3\etc\hadoop\hadoop-env.cmd,修改其中的JAVA_HOME(目录是C:\Program Files\Java\jdk1.8.0_121,因为Program Files中存在空格,所以出现错误)只需要用
PROGRA~1代替Program Files即可
四、MR作业的流程分析
---------------------------------------------------------------------
AppMain() //程序main主函数入口
-->job.waitForCompletion(true) //等待直到MR作业完成
---> job.submit() //job,提交作业
--> (JobSubmitter)submitter.submitJobInternal(Job.this, cluster) //内部提交
--> (ClientProtocol)submitClient.submitJob() //真正的提交
--> LocalJobRunner.submitJob() //本地作业提交
--> (LocalJobRunner.Job) new job() //新建LocalJobRunner$Job对象
--> LocalJobRunner.Job.run() //new job() 过程中,开启线程run()
--> SplitMetaInfoReader.readSplitMetaInfo() //读取数据分割元数据等信息。进行任务分配
--> 开始顺序执行任务,首先是map任务,然后是reduce任务
--> runTasks(mapRunnables, mapService, "map"); //开始运行任务集合
--> runTasks(reduceRunnables, reduceService, "map");
--> 循环执行 service.submit(r) //提交分任务(Map任务)-- 之前有几个spilt,这里就有几个maptask
--> 每循环一次,就会开启一个分线程
-->LocalJobRunner.Job.MapTaskRunnable.run()//service.submit(r)开启的线程的run()函数
--> map.run() //运行map任务。此时会开启分线程去运行map task
--> (MapTask) runNewMapper()
--> (MyMapper)mapper.run(mapperContext); //此处开始执行自定义的MyMapper类的map方法,进行map过程
--> 一直运行,直到map过程结束,紧接着开始执行reduce
--> runTasks(reduceRunnables, reduceService, "reduce"); //开始执行reduce任务
--> 循环执行 service.submit(r); //提交分任务(Reduce任务)
--> reduce.run(localConf, Job.this); //运行reduce任务
--> shuffleConsumerPlugin = ReflectionUtils.newInstance() //反射加载shuffle类
--> shuffleConsumerPlugin.run() //开始shuffle
--> sortPhase.complete() //排序结束
--> runNewReducer(); //开始执行reduce任务
--> reducer.run(reducerContext);
--> MyReducer.reduce(); //此处开始执行自定义的reduce方法
--> 至此LocalJobRunner.Job.run()线程运行结束
--> (JobSubmitter)submitter.submitJobInternal(Job.this, cluster) //运行结束
--> waitForCompletion() 结束,返回0
--> app结束,整个mr作业结束
五、案例代码部分
----------------------------------------------------------------------------
**************************************
1.App入口函数类
**************************************
public class MaxTemperature {
private static String inPath = "file:///D:\\test\\in";
private static String outPath = "file:///D:\\test\\out5";
public static void main(String[] args) {
try
{
//开启一个MapReduce的JOB
Job job = Job.getInstance();
//设定app运行类(main类)
job.setJarByClass(MaxTemperature.class);
//设定job的名称
job.setJobName("Max temperature");
//设定数据输入路径
FileInputFormat.addInputPath(job, new Path(inPath));
//设定数据输出路径
FileOutputFormat.setOutputPath(job, new Path(outPath));
//设定MapClass
job.setMapperClass(MyMapper.class);
//设定ReduceClass
job.setReducerClass(MyReducer.class);
//设定输出key类型
job.setOutputKeyClass(Text.class);
//设定输出value类型
job.setOutputValueClass(IntWritable.class);
//等待job结束
job.waitForCompletion(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
**************************************
2.Map类
**************************************
/**
* Map类
* @author Administrator
*
*/
public class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
private static final int MISSING = 9999;
/**
* Map方法,对数据进行map处理
*/
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
//将Writable转化成普通java对象
long index = key.get(); //获取输入的一行数据的索引
String line = value.toString(); //获取输入的一样数据的内容
//获取感兴趣的值:年份和温度
String year = line.substring(15, 19);
int airTemperature;
if (line.charAt(87) == '+')
{
// parseInt doesn't like leading plus signs
airTemperature = Integer.parseInt(line.substring(88, 92));
}
else
{
airTemperature = Integer.parseInt(line.substring(87, 92));
}
//空气质量
String quality = line.substring(92, 93);
//分析结果正确与否,并且输出年份和气温(输出到reduce中进行下一步计算)
if (airTemperature != MISSING && quality.matches("[01459]")) {
context.write(new Text(year), new IntWritable(airTemperature));
}
}
}
**************************************
3.Reduce类
**************************************
/**
* Reduce类
* @author Administrator
*
*/
public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
@Override
public void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException
{
//获取气温中的最大值
int maxValue = Integer.MIN_VALUE;
for (IntWritable value : values)
{
maxValue = Math.max(maxValue, value.get());
}
context.write(key, new IntWritable(maxValue));
}
}