Hadoop MapReduce & Yarn 详解

1. MapReduce

1. MapReduce概述

Hadoop MapReduce 是一个分布式计算框架,用于编写批处理应用程序。编写好的程序可以提交到 Hadoop 集群上用于并行处理大规模的数据。

概念

  • 面向批处理的分布式计算框架
  • 一种编程模型:MapReduce程序被分为Map(映射)阶段和Reduce(化简)阶段

核心思想

  • 分而治之,并行计算
  • 移动计算,而非移动数据

特点

  • 计算跟着数据走
  • 良好的扩展性:计算能力随着节点数增加,近似线性递增
  • 高容错
  • 状态监控
  • 适合海量数据的离线批处理
  • 降低了分布式编程的门槛

适用场景

  • 数据统计,如:网站的PV、UV统计
  • 搜索引擎构建索引
  • 海量数据查询
  • 复杂数据分析算法实现

不适用场景

  • OLAP
    • 要求毫秒或秒级返回结果
  • 流计算
    • 流计算的输入数据集是动态的,而MapReduce是静态的
  • DAG计算
    • 多个任务之间存在依赖关系,后一个的输入是前一个的输出,构成有向无环图DAG
    • 每个MapReduce作业的输出结果都会落盘,造成大量磁盘IO,导致性能非常低下

2. 编程模型

MapReduce 作业通过将输入的数据集拆分为独立的块,这些块由 map 以并行的方式处理,框架对 map 的输出进行排序,然后输入到 reduce 中。MapReduce 框架专门用于 <key,value> 键值对处理,它将作业的输入视为一组 <key,value> 对,并生成一组 <key,value> 对作为输出。输入和输出的 key 和 value 都必须实现Writable 接口。

(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)

Job & Task(作业与任务)

  • 作业是客户端请求执行的一个工作单元
    • 包括输入数据、MapReduce程序、配置信息
  • 任务是将作业分解后得到的细分工作单元
    • 分为Map任务和Reduce任务

MapReduce

input : 读取文本文件

Split:MapReduce会根据输入文件计算输入分片(input split),每个输入分片(input split)针对一个map任务

  • 输入数据被划分成等长的小数据块,称为输入切片(Input Split),简称切片
  • Split是逻辑概念,仅包含元数据信息,如:数据的起始位置、长度、所在节点等
  • 每个Split交给一个Map任务处理,Split的数量决定Map任务的数量
  • Split的大小默认等于Block大小
  • Split的划分方式由程序设定,Split与HDFS Block没有严格的对应关系
  • Split越小,负载越均衡,但集群开销越大;Split越大,Map任务数少,集群的计算并发度降低

Map阶段:就是程序员编写好的map函数了,因此map函数效率相对好控制,而且一般map操作都是本地化操作也就是在数据存储节点上进行

  • 由若干Map任务组成,任务数量由Split数量决定
  • 输入:Split切片(key-value),输出:中间计算结果(key-value)

Shuffle阶段:由于 Mapping 操作可能是在不同的机器上并行处理的,所以需要通过 shuffling 将相同 key 值的数据分发到同一个节点上去合并,这样才能统计出最终的结果

  • Map、Reduce阶段的中间环节,是虚拟阶段
  • 负责执行Partition(分区)、Sort(排序)、Spill(溢写)、Merge(合并)、 Fetch(抓取)等工作
  • Partition决定了Map任务输出的每条数据放入哪个分区,交给哪个Reduce任务处理
  • Reduce任务的数量决定了Partition数量
  • Partition编号 = Reduce任务编号 =“key hashcode % reduce task number”(%为取模/取余数)
  • 哈希取模的作用:将一个数据集随机均匀分成若干个子集
  • 避免和减少Shuffle是MapReduce程序调优的重点

Reduce阶段:开始执行reduce任务,最后结果保留在hdfs上

  • 由若干Reduce任务组成,任务数量由程序指定
  • 输入:Map阶段输出的中间结果(key-value),输出:最终结果(key-value)

Shuffle过程

Shuffle

Map端

  • Partition/Sort:Map任务将中间结果写入专用内存缓冲区Buffer(默认100M),同时进行Partition和Sort(先按“key hashcode % reduce task number”对数据进行分区,分区内再按key排序)
  • Spill:当Buffer的数据量达到阈值(默认80%)时,将数据溢写(Spill)到磁盘的一个临时文件中,文件内数据先分区后排序
  • Merge:Map任务结束前,将多个临时文件合并(Merge)为一个Map输出文件,文件内数据先分区后排序

Reduce端

  • Fetch:Reduce任务从多个Map输出文件中主动抓取(Fetch)属于自己的分区数据,先写入Buffer,数据量达到阈值后,溢写到磁盘的一个临时文件中
  • Merge:数据抓取完成后,将多个临时文件合并为一个Reduce输入文件,文件内数据按key排序

Combiner

combiner 是 map 运算后的可选操作,它实际上是一个本地化的 reduce 操作,它主要是在 map 计算出中间文件后做一个简单的合并重复 key 值的操作。这里以词频统计为例:

map 在遇到一个 hadoop 的单词时就会记录为 1,但是这篇文章里 hadoop 可能会出现 n 多次,那么 map 输出文件冗余就会很多,因此在 reduce 计算前对相同的 key 做一个合并操作,那么需要传输的数据量就会减少,传输效率就可以得到提升。

但并非所有场景都适合使用 combiner,使用它的原则是 combiner 的输出不会影响到 reduce 计算的最终输入,例如:求总数,最大值,最小值时都可以使用 combiner,但是做平均值计算则不能使用 combiner。

Partitioner

partitioner 可以理解成分类器,将 map 的输出按照 key 值的不同分别分给对应的 reducer,支持自定义实现。

3. 案例

public class WordCountApp {

    // 这里为了直观显示参数 使用了硬编码,实际开发中可以通过外部传参
    private static final String HDFS_URL = "hdfs://192.168.0.107:8020";
    private static final String HADOOP_USER_NAME = "root";

    public static void main(String[] args) throws Exception {

        //  文件输入路径和输出路径由外部传参指定
        if (args.length < 2) {
            System.out.println("Input and output paths are necessary!");
            return;
        }

        // 需要指明 hadoop 用户名,否则在 HDFS 上创建目录时可能会抛出权限不足的异常
        System.setProperty("HADOOP_USER_NAME", HADOOP_USER_NAME);

        Configuration configuration = new Configuration();
        // 指明 HDFS 的地址
        configuration.set("fs.defaultFS", HDFS_URL);

        // 创建一个 Job
        Job job = Job.getInstance(configuration);

        // 设置运行的主类
        job.setJarByClass(WordCountApp.class);

        // 设置 Mapper 和 Reducer
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);

        // 设置 Mapper 输出 key 和 value 的类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        // 设置 Reducer 输出 key 和 value 的类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        // 如果输出目录已经存在,则必须先删除,否则重复运行程序时会抛出异常
        FileSystem fileSystem = FileSystem.get(new URI(HDFS_URL), configuration, HADOOP_USER_NAME);
        Path outputPath = new Path(args[1]);
        if (fileSystem.exists(outputPath)) {
            fileSystem.delete(outputPath, true);
        }

        // 设置作业输入文件和输出文件的路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, outputPath);

        // 将作业提交到群集并等待它完成,参数设置为 true 代表打印显示对应的进度
        boolean result = job.waitForCompletion(true);

        // 关闭之前创建的 fileSystem
        fileSystem.close();

        // 根据作业结果,终止当前运行的 Java 虚拟机,退出程序
        System.exit(result ? 0 : -1);

    }
}

2. YARN

1. YARN简介

MapReduce存在先天缺陷(Hadoop 1.X)

  • 身兼两职:计算框架 + 资源管理系统
  • JobTracker
    • 既做资源管理,又做任务调度
    • 任务太重,开销过大
    • 存在单点故障
  • 资源描述模型过于简单,资源利用率较低
    • 仅把Task数量看作资源,没有考虑CPU和内存
    • 强制把资源分成Map Task Slot和Reduce Task Slot
  • 扩展性较差,集群规模上限4K
  • 源码难于理解,升级维护困难

YARN

Apache YARN (Yet Another Resource Negotiator) 是 hadoop 2.0 引入的集群资源管理系统。用户可以将各种服务框架部署在 YARN 上,由 YARN 进行统一地管理和资源分配。

  • YARN,Yet Another Resource Negotiator,另一种资源管理器
  • 分布式通用资源管理系统
  • 设计目标:聚焦资源管理、通用(适用各种计算框架)、高可用、高扩展

2. YARN原理

  • Master/Slave架构
  • 将JobTracker的资源管理、任务调度功能分离
  • 三种角色:ResourceManager(Master)、NodeManager(Slave)、ApplicationMaster

YARN架构

ResourceManager

ResourceManager 通常在独立的机器上以后台进程的形式运行,它是整个集群资源的主要协调者和管理者。ResourceManager 负责给用户提交的所有应用程序分配资源,它根据应用程序优先级、队列容量、ACLs、数据位置等信息,做出决策,然后以共享的、安全的、多租户的方式制定分配策略,调度集群资源。

主要功能

  • 统一管理集群的所有资源
  • 将资源按照一定策略分配给各个应用(ApplicationMaster)
  • 接收NodeManager的资源上报信息

核心组件

  • 用户交互服务(User Service)
  • NodeManager管理
  • ApplicationMaster管理
  • Application管理
  • 安全管理
  • 资源管理

NodeManager

NodeManager 是 YARN 集群中的每个具体节点的管理者。主要负责该节点内所有容器的生命周期的管理,监视资源和跟踪节点健康。具体如下:

  • 启动时向 ResourceManager 注册并定时发送心跳消息,等待 ResourceManager 的指令;
  • 维护 Container 的生命周期,监控 Container 的资源使用情况;
  • 管理任务运行时的相关依赖,根据 ApplicationMaster 的需要,在启动 Container 之前将需要的程序及其依赖拷贝到本地。

主要功能

  • 管理单个节点的资源
  • 向ResourceManager汇报节点资源使用情况
  • 管理Container的生命周期

核心组件

  • NodeStatusUpdater
  • ContainerManager
  • ContainerExecutor
  • NodeHealthCheckerService
  • Security
  • WebServer

ApplicationMaster

在用户提交一个应用程序时,YARN 会启动一个轻量级的进程 ApplicationMaster。ApplicationMaster 负责协调来自 ResourceManager 的资源,并通过 NodeManager 监视容器内资源的使用情况,同时还负责任务的监控与容错。具体如下:

  • 根据应用的运行状态来决定动态计算资源需求;
  • 向 ResourceManager 申请资源,监控申请的资源的使用情况;
  • 跟踪任务状态和进度,报告资源的使用情况和应用的进度信息;
  • 负责任务的容错。

主要功能

  • 管理应用程序实例
  • 向ResourceManager申请任务执行所需的资源
  • 任务调度和监管

实现方式

  • 需要为每个应用开发一个AM组件
  • YARN提供MapReduce的ApplicationMaster实现
  • 采用基于事件驱动的异步编程模型,由中央事件调度器统一管理所有事件
  • 每种组件都是一种事件处理器,在中央事件调度器中注册

Contain

Container 是 YARN 中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等。当 AM 向 RM 申请资源时,RM 为 AM 返回的资源是用 Container 表示的。YARN 会为每个任务分配一个 Container,该任务只能使用该 Container 中描述的资源。 ApplicationMaster 可在 Container 内运行任何类型的任务。例如,MapReduce ApplicationMaster 请求一个容器来启动 map 或 reduce 任务,而 Giraph ApplicationMaster 请求一个容器来运行 Giraph 任务。

概念:Container封装了节点上进程的相关资源,是YARN中资源的抽象

分类:运行ApplicationMaster的Container 、运行应用任务的Container

3. YARN工作机制

YARN工作机制

  1. Client 提交作业到 YARN 上
  2. Resource Manager 选择一个 Node Manager,启动一个 Container 并运行 Application Master 实例
  3. Application Master 根据实际需要向 Resource Manager 请求更多的 Container 资源(如果作业很小, 应用管理器会选择在其自己的 JVM 中运行任务)
  4. Application Master 通过获取到的 Container 资源执行分布式计算

原理详述

  1. 作业提交
    client 调用 job.waitForCompletion 方法,向整个集群提交 MapReduce 作业 (第 1 步) 。新的作业 ID(应用 ID) 由资源管理器分配 (第 2 步)。作业的 client 核实作业的输出, 计算输入的 split, 将作业的资源 (包括 Jar 包,配置文件, split 信息) 拷贝给 HDFS(第 3 步)。 最后, 通过调用资源管理器的 submitApplication() 来提交作业 (第 4 步)。

  2. 作业初始化
    当资源管理器收到 submitApplciation() 的请求时, 就将该请求发给调度器 (scheduler), 调度器分配 container, 然后资源管理器在该 container 内启动应用管理器进程, 由节点管理器监控 (第 5 步)。
    MapReduce 作业的应用管理器是一个主类为 MRAppMaster 的 Java 应用,其通过创造一些 bookkeeping 对象来监控作业的进度, 得到任务的进度和完成报告 (第 6 步)。然后其通过分布式文件系统得到由客户端计算好的输入 split(第 7 步),然后为每个输入 split 创建一个 map 任务, 根据 mapreduce.job.reduces 创建 reduce 任务对象。

  3. 任务分配
    如果作业很小, 应用管理器会选择在其自己的 JVM 中运行任务。
    如果不是小作业, 那么应用管理器向资源管理器请求 container 来运行所有的 map 和 reduce 任务 (第 8 步)。这些请求是通过心跳来传输的, 包括每个 map 任务的数据位置,比如存放输入 split 的主机名和机架 (rack),调度器利用这些信息来调度任务,尽量将任务分配给存储数据的节点, 或者分配给和存放输入 split 的节点相同机架的节点。

  4. 任务运行
    当一个任务由资源管理器的调度器分配给一个 container 后,应用管理器通过联系节点管理器来启动 container(第 9 步)。任务由一个主类为 YarnChild 的 Java 应用执行, 在运行任务之前首先本地化任务需要的资源,比如作业配置,JAR 文件, 以及分布式缓存的所有文件 (第 10 步。 最后, 运行 map 或 reduce 任务 (第 11 步)。
    YarnChild 运行在一个专用的 JVM 中, 但是 YARN 不支持 JVM 重用。

  5. 进度和状态更新
    YARN 中的任务将其进度和状态 (包括 counter) 返回给应用管理器, 客户端每秒 (通 mapreduce.client.progressmonitor.pollinterval 设置) 向应用管理器请求进度更新, 展示给用户。

  6. 作业完成
    除了向应用管理器请求作业进度外, 客户端每 5 分钟都会通过调用 waitForCompletion() 来检查作业是否完成,时间间隔可以通过 mapreduce.client.completion.pollinterval 来设置。作业完成之后, 应用管理器和 container 会清理工作状态, OutputCommiter 的作业清理方法也会被调用。作业的信息会被作业历史服务器存储以备之后用户核查。

高可用

ResourceManager高可用

  • 1个Active RM、多个Standby RM
  • 宕机后自动实现主备切换
  • ZooKeeper的核心作用
    • Active节点选举
    • 恢复Active RM的原有状态信息
  • 重启AM,杀死所有运行中的Container
  • 切换方式:手动、自动

YARN高可用

最后

大家可以关注我的微信公众号一起学习进步。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值