Hadoop2源码分析-MapReduce篇

2024-05-27 12:32

本文主要是介绍Hadoop2源码分析-MapReduce篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.概述

  前面我们已经对Hadoop有了一个初步认识,接下来我们开始学习Hadoop的一些核心的功能,其中包含mapreduce,fs,hdfs,ipc,io,yarn,今天为大家分享的是mapreduce部分,其内容目录如下所示:

  • MapReduce V1
  • MapReduce V2
  • MR V1和MR V2的区别
  • MR V2的重构思路

  本篇文章的源码是基于hadoop-2.6.0-src.tar.gz来完成的。代码下载地址,请参考《Hadoop2源码分析-准备篇》。

2.MapReduce V1

  下面我们给出第一代的MapReduce的架构图,如下所示:

  上图描述了第一代MapReduce框架的流程以及设计思路,下面为大家解释下这张图的具体含义:

  • 当我们编写完MR作业后,需要通过JobClient来提交一个job,提交的信息会发送到JobTracker模块,这个模块是第一代MapReduce计算框架的核心之一,它负责与集群中的其他节点维持心跳,为提交的作业分配资源,管理提交的作业的正常运作(失败,重启等)。
  • 第一代MapReduce的另一个核心的功能是TaskTracker,在各个TaskTracker安装节点上,它的主要功能是监控自己所在节点的资源使用情况。
  • TaskTracker监控当前节点的Tasks的运行情况,其中包含Map Task和Reduce Task,最后由Reduce Task到Reduce阶段,将结果输送到HDFS的文件系统中;其中的具体流程如图中描述的1-7步骤。TaskTracker在监控期间,需要把这些信息通过心跳机制发送给JobTracker,JobTracker收集到这些信息后,给新提交的作业分配其他的资源,避免重复资源分配。

  可以看出,第一代的MapReduce架构简单清晰,在刚面世的那几年,也曾获得总多企业的支持和认可。但随着分布式集群的规模和企业业务的增长,第一代框架的问题也逐渐暴露出来,主要有以下问题:

  • JobTracker是第一代MapReduce的入口点,若是JobTracker服务宕机,整个服务将会瘫痪,存在单点问题。
  • JobTracker负责的事情太多,完成来太多的任务,占用过多的资源,当Job数非常多的时候,会消耗很多内存,容易出现性能瓶颈。
  • 对TaskTracker而言,Task担当的角色过于简单,没有考虑到CPU及内存的使用情况,若存在多个大内存的Task被集中调度,容易出现内存溢出。
  • 另外,TaskTracker把资源强制分为map task slot和reduce task slot,若是MR任务中只存在其中一个(map或是reduce),会出现资源浪费的情况,资源利用率低。
  • 从开发人员的角度来说,源码分析的时候,阅读性不够友好,代码量大,任务不清晰,给开发人员在修复BUG和维护的时候增大了难度。

3.MapReduce V2

  在Hadoop V2中,加入了YARN的概念,所以MapReduce V2的架构和MapReduce V1的架构有些许的变化,如下图所示:

  从上图中,我们可以清晰的看出,架构重构的基本思想在于将JobTracker的两个核心的功能单独分离成独立的组件了。分离后的组件分别为资源管理(Applications Manager)和任务调度器(Resource Scheduler)。新的资源管理器(Resource Manager)管理整个系统的资源分配,而每一个Node Manager下的App Master(Application Master)负责对应的调度和协调工作,而在实际中,App Master从Resource Manager上获得资源,让Node Manager来协同工作和任务监控。

  从图中我们可以看出,Resource Manager是支持队列分层的,这些队列可以从集群中获取一定比例的资源,也就是说Resource Manager可以算得上是一个调度器,它在执行的过程当中本身不负责对应用的监控和状态的定位跟踪。

  Resource Manager在内存,CPU,IO等方面是动态分配的,相比第一代MapReduce计算框架,在资源使用上大大的加强了资源使用的灵活性。上图中的Node Manager是一个代理框架,负责应用程序的执行,监控应用程序的资源利用率,并将信息上报给资源管理器。另外,App Master所担当的角色职责包含:在运行任务是,向任务调度器动态的申请资源,对应用程序的状态进行监控,处理异常情况,如若出现问题,会在其他节点进行重启。

4.MR V1和MR V2的区别

  在和大家分析完 MR V1 和 MR V2 的架构后,我们来看看二者有哪些变化。在MR V2版本中,大部分的API接口都是兼容的保留下来,MR V1中的JobTracker和TaskTracker被替换成相应的Resource Manager,Node Manager。对比于MR V1中的Task的监控,重启等内热都交由App Master来处理,Resource Manager提供中心服务,负责资源的分配与调度。Node Manager负责维护Container的状态,并将收集的信息上报给Resource Manager,以及负责和Resource Manager维持心跳。

  MR V2中加入Yarn的概念后,体现以下设计优点:

  • 减少来资源消耗,让监控每一个作业更加分布式了。
  • 能够支持更多的变成模型,如:Spark,Storm,以及其他待开发的编程模型。
  • 将资源以内存量的概念来描述,比MR V1中的slot更加合理。

  另外,在工程目录结构也有了些许的变化,如下表所示:

改变目录 MR V1 MR V2 描述
配置文件 ${HADOOP_HOME}/conf ${HADOOP_HOME}/etc/hadoop MR V2中的配置文件路径修改为etc/hadoop目录下
脚本 ${HADOOP_HOME}/bin ${HADOOP_HOME}/sbin和${HADOOP_HOME}/bin 在MR V2中启动,停止等命令都位于sbin目录下,操作hdfs的命令存放在bin目录下
JAVA_HOME ${HADOOP_HOME}/conf/hadoop-env.sh ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh和${HADOOP_HOME}/etc/hadoop/yarn-env.sh 在MR V2中需要同时在hadoop-env.sh和yarn-env.sh中配置JDK的路径

  由于添加Yarn特性,与第一代MR的框架变化较大,第一代的核心配置文件许多项也在新框架中摒弃了,具体新框架的核心配置文件信息,请参考《配置高可用的Hadoop平台》。

5.MR V2的重构思路

  在V2中的MapReduce重构的思路主要有以下几点:

  • 层次化的管理:分层级对资源的调度和分配进行管理。
  • 资源管理方式:由第一代的slot作为资源单位元,调整为更加细粒的内存单位元。
  • 编程模型拓展:V2版的设计支持除MapReduce以外的编程模型。

  MapReduce:WordCount V2,代码如下:

复制代码
package cn.hdfs.mapreduce.example;import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.util.StringUtils;/**
* @date Apr 17, 2015
*
* @author dengjie
*/
public class WordCount2 {public static class TokenizerMapperextends Mapper<Object, Text, Text, IntWritable>{static enum CountersEnum { INPUT_WORDS }private final static IntWritable one = new IntWritable(1);private Text word = new Text();private boolean caseSensitive;private Set<String> patternsToSkip = new HashSet<String>();private Configuration conf;private BufferedReader fis;@Overridepublic void setup(Context context) throws IOException,InterruptedException {conf = context.getConfiguration();caseSensitive = conf.getBoolean("wordcount.case.sensitive", true);if (conf.getBoolean("wordcount.skip.patterns", true)) {URI[] patternsURIs = Job.getInstance(conf).getCacheFiles();for (URI patternsURI : patternsURIs) {Path patternsPath = new Path(patternsURI.getPath());String patternsFileName = patternsPath.getName().toString();parseSkipFile(patternsFileName);}}}private void parseSkipFile(String fileName) {try {fis = new BufferedReader(new FileReader(fileName));String pattern = null;while ((pattern = fis.readLine()) != null) {patternsToSkip.add(pattern);}} catch (IOException ioe) {System.err.println("Caught exception while parsing the cached file '"+ StringUtils.stringifyException(ioe));}}@Overridepublic void map(Object key, Text value, Context context) throws IOException, InterruptedException {String line = (caseSensitive) ?value.toString() : value.toString().toLowerCase();for (String pattern : patternsToSkip) {line = line.replaceAll(pattern, "");}StringTokenizer itr = new StringTokenizer(line);while (itr.hasMoreTokens()) {word.set(itr.nextToken());context.write(word, one);Counter counter = context.getCounter(CountersEnum.class.getName(),CountersEnum.INPUT_WORDS.toString());counter.increment(1);}}}public static class IntSumReducerextends Reducer<Text,IntWritable,Text,IntWritable> {private IntWritable result = new IntWritable();public void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {int sum = 0;for (IntWritable val : values) {sum += val.get();}result.set(sum);context.write(key, result);}}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();GenericOptionsParser optionParser = new GenericOptionsParser(conf, args);String[] remainingArgs = optionParser.getRemainingArgs();if (!(remainingArgs.length != 2 || remainingArgs.length != 4)) {System.err.println("Usage: wordcount <in> <out> [-skip skipPatternFile]");System.exit(2);}Job job = Job.getInstance(conf, "word count");job.setJarByClass(WordCount2.class);job.setMapperClass(TokenizerMapper.class);job.setCombinerClass(IntSumReducer.class);job.setReducerClass(IntSumReducer.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(IntWritable.class);List<String> otherArgs = new ArrayList<String>();for (int i=0; i < remainingArgs.length; ++i) {if ("-skip".equals(remainingArgs[i])) {job.addCacheFile(new Path(remainingArgs[++i]).toUri());job.getConfiguration().setBoolean("wordcount.skip.patterns", true);} else {otherArgs.add(remainingArgs[i]);}}FileInputFormat.addInputPath(job, new Path(otherArgs.get(0)));FileOutputFormat.setOutputPath(job, new Path(otherArgs.get(1)));System.exit(job.waitForCompletion(true) ? 0 : 1);}
}
复制代码

  Spark:WordCount,代码如下:

复制代码
package com.hdfs.spark.example/**
* @date Apr 17, 2015
*
* @author dengjie
*/
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._/**
* 统计字符出现次数
*/
object WordCount {def main(args: Array[String]) {if (args.length < 1) {System.err.println("Usage: <file>")System.exit(1)}val conf = new SparkConf()val sc = new SparkContext(conf)     val line = sc.textFile(args(0))      line.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_+_).collect().foreach(println)      sc.stop()   
} 
}
复制代码

这篇关于Hadoop2源码分析-MapReduce篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1007487

相关文章

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细

PostgreSQL 序列(Sequence) 与 Oracle 序列对比差异分析

《PostgreSQL序列(Sequence)与Oracle序列对比差异分析》PostgreSQL和Oracle都提供了序列(Sequence)功能,但在实现细节和使用方式上存在一些重要差异,... 目录PostgreSQL 序列(Sequence) 与 oracle 序列对比一 基本语法对比1.1 创建序

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

慢sql提前分析预警和动态sql替换-Mybatis-SQL

《慢sql提前分析预警和动态sql替换-Mybatis-SQL》为防止慢SQL问题而开发的MyBatis组件,该组件能够在开发、测试阶段自动分析SQL语句,并在出现慢SQL问题时通过Ducc配置实现动... 目录背景解决思路开源方案调研设计方案详细设计使用方法1、引入依赖jar包2、配置组件XML3、核心配

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字