作业提交与初始化过程分析

1 作业提交与初始化过程分析

一个MapReduce作业的提交与初始化的过程为:从用户输入提交作业命令到作业初始化的整个过程。此过程设计JobClinet、JobTracker和TaskScheduler三个组件,其功能分别为准备运行环境、接受作业以及初始化作业。

1.1 作业提交与初始化概述

总体而言,作业提交过程比较简单,它主要为后续作业执行准备环境,主要涉及创建目录、上传文件等操作;而一旦用户提交作业后,JobTracker端便会对作业进行初始化。作业初始化的主要工作是根据输入数据量和作业配置参数将作业分解成若干个Map Task以及Reduce Task,并添加到相关数据结构中,以等待后续被调度执行。总之,可将作业提交与初始化过程分以下四个步骤:

1)用户使用Hadoop提供的Shell命令提交作业;
2)JobClient按照作业配置信息(JonConf)将作业运行需要的全部文件上传到JobTracker文件系统(通常为HDFS)的某个目录下;
3)JobClient调用RPC接口向JobTracker提交作业;
4)JobTracker接收到作业后,将其告知TaskScheduler,由TaskScheduler对作业进行初始化;

步骤2)中,将作业相关文件(包括应用程序jar包、xml文件及其依赖的文件等,即作业文件)上传到HDFS上,主要基于以下考虑:

HDFS是一个分布式文件系统,Hadoop集群中任何一个节点可以直接从该系统中下载文件。即HDFS上所有文件都是节点间共享的(不考虑文件权限);
作业文件是运行Task所必须的。它们一旦被上传到HDFS上后,任何一个Task只需知道存放路径便可以下载到自己的工作目录中使用,因而可看作一种非常简便的文件共享方式。

1.2 作业提交过程详解

Hadoop提供的三种应用程序开发方法包括原始Java API、Hadoop Streaming和Hadoop Pipes,考虑到后两种方法的底层实际上均调用了Java API,下面以Java MapReduce程序为例叙述作业提交过程。

1.2.1 执行Shell命令

假设用户采用java语言编写了一个MapReduce程序,并将其打包成xxx.jar,然后通过对应的命令提交作业。
当用户输入对应命令后,bin/hadoop脚本将根据“jar”命令将作业交给RunJar类处理。
RunJar类中的main函数经解压jar包和设置环境变量后,将运行参数传递给MapReduce程序,并运行之。
用户的MapReduce程序已经配置了作业运行时需要的各种信息(如Mapper类、Reducer类、Reduce Task个数等),它最终在main函数中调用JobClient.runjob函数(新MapReduce API则使用job.waitForCompletion(true)函数)提交作业,这之后会依次经过图5-2所示的函数调用顺序才会将作业提交到JobTracker端。

这里写图片描述

1.2.2 作业文件上传

JobClient将作业提交到JobTracker端之前,需要进行一些初始化工作,包括:获取作业ID,创建HDFS目录,上传作业文件以及生成Split文件等。这些工作由函数JobClient.submitJobInternal(job)实现,具体流程如下图:

MapReduce作业文件的上传与下载由DistributedCache工具完成的。它是Hadoop为方便用户进行应用程序开发而设计的数据分发工具。其整个流程对用户而言是透彻的,即用户只需在提交作业时指定文件位置,至于这些文件的分发(需广播到各个TaskTracker上以运行Task),完全由DistributedCache工具完成,不需要用户参与。

通常而言,对于一个典型的java编写的Java MapReduce作业,可能包含以下资源。

程序jar包:用户用Java编写的MapReduce应用程序jar包。
作业配置文件:描述MapReduce应用程序的配置信息(根据JobConf对象生成的xml文件)。
依赖的第三方jar包:应用程序依赖的第三方jar包,提交作业时用参数“-libjars”指定。
依赖的归档文件:应用程序中用到多个文件,可直接打包成归档文件(通常为一些压缩文件),提交作业时用参数“-archives”指定。
依赖的普通文件:应用程序中可能用到普通文件,比如文本格式的字典文件,提交作业时用参数“-files”指定。

上述所有文件在JobClient端被提交到HDFS上,涉及到的目录如下表所示:

作业属性属性值说明
mapreduce.jobtracker.staging.root.dir${hadoop.tmp.dir}/mapred/stagingHDFS上作业文件的上传目录,由管理员配置
mapreduce.job.dir mapreduce.jobtracker.staging.root.dir/ {usr}/.staging/ jobID| {user}的作业{jobId}相关文件存放目录

MapReduce作业在HDFS中的目录组织结构如下图:

这里写图片描述

文件上传完毕后,会将目录信息保存到作业配置对象JobConf中,其对应的作业属性如下表:

作业属性说明
mapred.cache.files作业依赖的普通文件在HDFS上的存放路径
mapred.job.classpath.archives作业依赖的jar包在HDFS上的存放路径
mapred.cache.archives作业依赖的压缩文件在HDFS上册存放路径
mapreduce.job.cache.files.visibilities作业依赖的普通文件的可见性。如果是public可见性,则为true,否则为false
mapreduce.job.cache.archives.visibilities作业依赖的归档文件的可见性。如果是public级别的可见性,则为true,否则为false
mapred.cache.files.timestamps作业依赖的普通文件的最后一次修改时间的时间戳
mapred.chache.archives.timestamps作业依赖的压缩文件的最后一次修改时间的时间戳
mapred.cache.files.filesizes作业依赖的普通文件的大小
mapred.cache.archives.filesizes作业依赖的归档文件的大小
mapred.jar用户应用程序jar路径

DistributedCache将文件分为两种可见级别,分别是private级别和public级别。其中,private级别文件只会被当前用户使用,不能与其他用户共享;而public级别文件则不同,它们在每个节点上保存一份,可被本节点上的所有作业和用户共享;而public级别文件则不同,它们在每个节点上保存一份,可被本节点上的所有作业和用户共享,这样可以大大降低文件复制代价,提高了作业运行效率。一个文件/目录要成为public级别文件/目录,需同时满足以下两个条件:

该文件/目录对所有用户/用户组均有可读权限
该文件/目录的父目录、父目录的父目录…对所有用户/用户组有可执行权限。

作业文件上传到HDFS后,可能会有大量节点同时从HDFS上下载这些文件,进而产生文件访问热点现象,造成性能瓶颈。为此,JobClient上传这些文件时会调高它们的副本数(由参数mapred.submit.replication指定,默认是10)以通过分摊负载方式避免产生访问热点

1.2.3 产生InputSplit文件

用户提交MapReduce作业后,JobClient会调用InputFormat的getSplit方法生成InputSplit相关信息。该信息包括两部分:InputSplit元数据信息和原始InputSplit信息。其中,第一部分将被JobTracker使用,用以生成Task本地性(Task Locality)相关的数据结构;第二部分则将被Map Task初始化时使用,用于获取自己要处理的数据。这两部信息分别被保存到目录 mapreduce.jobtracker.staging.root.dir/ {user}/.staging/${JobId}下的文件job.splitmetainfo和job.split中。

InputSplit相关操作放在包org.apache.hadoop.mapreduce.split中,主要包含三个类JobSplit、JobSplitWriter和SplitMetaInfoReader。

JobSplit封装了读写InputSplit相关的基础类,主要包括以下三个。

SplitMetaInfo:描述一个InputSplit的元数据信息。所有InputSplit对应的SplitMetaInfo将被保存到文件job.splitmetainfo中。该文件内容一次为:一个用户表示InputSplit元数据文件头部的字符串“META-SP”,文件版本号splitVersion(当前值是1),作业对应的InputSplit数目length,最后是length个InputSplit对应的SplitMetaInfo信息。
作业在JobTracker端初始化时,需读取job.splitmetainfo文件创建Map Task。
TaskSplitMetaInfo:用于保存InputSplit元信息的数据结构。
TaskSplitIndex:JobTracker想TaskTracker分配新任务时,TaskSplitIndex用于指定新任务待处理数据位置信息在文件job.split中的索引。

作业提交到JobTracker

JobClient最终调用RPC方法submitJob将作业提交到JobTracker端,在JobTracker.submitJob中,会依次进行一下操作:

  • 为作业创建JobInProgress对象
    JobTracker会为每个作业创建一个JobInProgress对象。该对象维护了作业的运行时信息。它在作业运行过程中一直存在,主要用于跟踪正在运行作业的运行状态和进度。

  • 检查用户是否具有指定队列的作业提交权限

  • 检查作业配置的内存使用量是否合理
    用户提交作业时,可分别用参数mapred.job.map.memory.mb和mapred.job.reduce.memory.mb指定Map Task和Reduce Task占用的内存量;而管理员可通过参数mapred.cluster.max.map.memory.mb和mapred.cluster.max.reduce.memory.mb限制用户配置的任务最大内存使用量,一旦用户配置的内存使用量超过系统限制,则作业提交失败。

  • 通知TaskScheduler初始化作业
    JobTracker收到作业后,并不会马上对其初始化,而是交给调度器,由它按照一定的策略对作业进行初始化。之所以不选择JobTracker而让调度器初始化,主要考虑到以下两个原因:

    • 作业一旦初始化后便会占用一定量的内存资源,为了防止大量初始化的作业排队等待调度而占用大量不必要的内存资源,Hadoop按照一定的策略选择性地初始化作业以节省内存资源;
    • 任务调度器的职责是根据每个节点的资源使用情况对其分配最合适的任务,而只有经过初始化的作业才有可能得到调度,因而将作业初始化策略嵌入到调度器中是一种比较合理的设计。

Hadoop的调度器是一个可插拔模块,用户可通过实现TaskScheduler接口设计自己的调度器。当前Hadoop默认的调度器是JobQueueTaskScheduler。它采用的调度策略是先来先服务(FIFO)。另外两个比较常用的调度器是Fair Scheduler和Capacity Scheduler。

JobTracker采用观察者设计模式(发布-订阅模式)将“提交新作业”这一事件告诉TaskScheduler

JobTracker启动时会根据配置参数构造相应的任务调度器,并调用它的start()方法进行初始化。此方法中,调度器会向JobTracker注册JobInProgressListener对象以监听作业的添加/删除/更新等事件。

1.3 作业初始化过程

调度器调用JobTracker.initJob()函数对新作业进行初始化。作业初始化的主要工作是构造Map Task和Reduce Task并对它们进行初始化。

Hadoop将每个作业分解成4种类型的任务,分别是Setup Task、Map Task、Reduce Task和Cleanup Task。它们的运行时信息有TaskInProgress类维护,故创建这些任务实际上是创建TaskInProgress对象。

上述4种任务的作用及创建过程如下:

Setup Task:作业初始化标识性任务。它进行一些非常简单的作业初始化工作,如将运行状态设置为“setup”,调用OutputCommitter.setupJob()函数等。该任务运行完成后,作业由PREP状态变为RUNNING状态,并开始运行Map Task。该类任务又被分为Map Setup Task和Reduce Setup Task两种,且每个作业各有一个。它们运行时分别占用一个Map slot和Reduce slot。由于这两种任务功能相同,因此有且只有一个可以获得运行的机会(即只要有一个开始运行,另一个马上被杀掉,具体哪一个运行,取决于当时存在的空闲slot种类及调度策略。
Map Task:Map阶段处理数据的任务。其数目及对应的处理数据分片由应用程序中的InputFormat组件确定。
Reduce Task:Reduce阶段处理数据的任务。其数目由用户通过参数mapred.reduce.tasks(默认数目为1)指定。考虑到Reduce Task能否运行依赖于Map Task的输出结果,故Hadoop刚开始只会调度Map Task,直到Map Task完成数目达到一定比例(由参数mapred.reduce.slotstart.complete.maps指定,默认是0.05,即5%)后,才考试调度Reduce Task。
Cleanup Task:作业结束标志性任务,主要完后一些作业清理工作,如删除作业运行过程中用到的一些临时目录(如temporary目录)。一旦该任务运行成功后,作业由RUNNING状态变为SUCCEEDED状态。

随着Hadoop的普及和衍化,发现引入Setup/Cleanup Task会拖慢作业执行进度且降低作业的可靠性,主要是因为Hadoop除了需保证每个Map/Reduce Task运行成功外,还要保证Setup、Cleanup Task成功。对于Map/Reduce Task而言,可通过推测执行机制避免出现“拖后腿”任务。然后,由于Setup/Cleanup不会出来任何数据,两种任务进度只有0%和100%两个值,从而使得推测式任务机制对之不适用。未解决该问题,后期版本的Hadoop将是否启用Setup/Cleanup Task变成了可配置的选项,用户可通过参数mapred.committer.job.set.cleanup.needed配置是否为作业创建Setup/Cleanup Task。

1.4 hadoop DistributedCache原理分析

DistributedCache是Hadoop为方便用户进行应用程序开发而设计的文件分发工具。它能够将只读的外部文件自动分发到各个节点上进行本地缓存,以便Task运行时加载使用。它的大体工作流程如下:用户提交作业后,Hadoop将由-files和-archives选项指定的文件复制到JobTracker的文件系统(一般为HDFS)中;之后,当某个TaskTracker收到该作业的第一个Task后,该任务将负责从JobTracker文件系统中将文件下载到本地磁盘进行缓存,这样后续的Task就可以直接在本地访问这些文件了。除了文件分发外,DistributedCache还可用于软件自动安装部署。如用户使用PHP语言编写了MapReduce程序,为了能够让程序成功运行,用户需要求运维人员在Hadoop集群的各个节点上提前安装好PHP解释器,而当需要升级PHP解释器时,可能需通知Hadoop运维人员进行一次升级,这使得软件升级变得非常麻烦。为了让软件升级变得更可控,用户可采用DistributedCache将PHP解释器分发到各个节点上,每次运行程序时,DistributedCache会检查PHP解释器被改过(如升级新版本),如果是,则会自动重新下载。

1.4.1 使用方法介绍

用户编写的MapReduce应用程序往往需要一些外部的资源,如粉刺程序需词表文件,或者依赖于三方的jar包。此时,希望每个Task初始化时能够加载这些文件,而DistributedCache正是为了完成该功能而提供的。

使用Hadoop DistributedCache通常有两种方法:调用相关API和设置命令行参数。

  • 调用相关API

    Hadoop DistributedCache允许用户分发归档文件(后缀为.zip、.jar、.tar、.tgz或者.tar、.gz的文件)和普通文件。
    使用Hadoop DistributedCache可分为3个步骤:

    步骤1:在HDFS上准备好文件(文本文件、压缩文件、jar包等),并按照文件可见级别设置目录/文件的访问权限;
    步骤2:调用相关API添加文件信息,这里主要是配置作业的JobConf对象;
    步骤3:在Mapper或者Reducer类中使用文件,Mapper或者Reducer开始运行前,各种文件已经下载到本地的工作目录中,直接调用文件读写API即可获取文件内容。

  • 设置命令行参数
    这是一种比较简单且灵活的方法,但是前提是用户编写MapReduce应用程序时实现了Tool接口支持常规选项。该方法包括两个步骤,其中第一个步骤与“调用相关API的在步骤1相同,而第二个步骤则是使用Shell命令提交作业。”

1.4.2 工作原理分析

Hadoop DistributedCache的主要功能是将作业文件分发到各个TaskTracker上,其流程分为4个步骤:

步骤1:用户提交作业后,DistributedCache将作业文件上传到HDFS上的固定目录中;
步骤2:JobTracker端的任务调度器将作业对应的任务分派到各个TaskTracker上;
步骤3:任何一个TaskTracker收到改作业的第一个任务后,由DistributedCache自动将文件缓存到本地目录下(对于后缀为.zip、.jar、.tar、.tgz或者.tar.gz的文件,会自动对其进行解压缩),然后开始启动该任务;
步骤4:对于TaskTracker接下来收到的任务,DistributedCache不会再重复为其下载文件,而是直接运行。

Hadoop DistributedCache的实现主要包括DistributedCache、TaskDistributedCacheManager和TrackerDistributedCacheManager这三个类。它们的功能如下:

DistributedCache类:可供用户直接使用的外部类。它提供一系列addXXX、setXXX和getXXX方法以配置作业需要借用DistributedCache分发的只读文件
TaskDistributedCacheManager类:Hadoop内部使用的类,用于管理一个作业相关的缓存文件
TrackerDistributedCacheManager类:Hadoop内部使用的类,用于管理一个TaskTracker上所有的缓存文件。它只用缓存public可见级别的文件,而对于private可见级别的文件,则有org.apache.hadoop.mapred包中的JobLocalizer类进行缓存。

1.5 总结

作业提交与初始化过程是指从用户输入提交作业命令到作业初始化的整个过程。该过程涉及Hadoop三个非常重要的组件,即JobClient、JobTracker和TaskScheduler

作业提交过程是为后续作业执行准备环境,涉及创建目录、上传文件等操作

作业初始化的主要工作是根据输入数据量和作业配置参数将作业分解成若干个Map Task以及Reduce Task,并添加到相关数据结构中,以等待后续被调度执行。

Hadoop DistributedCache是Hadoop**为方便用户进行应用程序开发而设计的数据分发工具。它能够将只读的大文件自动分发到各个节点上进行本地缓存,以便Task运行时加载使用。它将待分发的文件根据可见级别分为**public级别和private级别两种。其中public级别文件允许同一个TaskTracker上所有用户共享,而private级别文件只允许某个用户的所欲作业共享。

作业在JobTracker端经初始化后,会被存放到相关数据结构中等待被调度执行



- 参考文献:Hadoop技术内幕

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值