十、作业执行及环境:
MRAppMaster 执行Mapper、Reducer作业时,会将task作为它的子进程,运行在独立的jvm中。
child-task 继承MRAppMaster 的运行环境。用户可以通过
mapreduce.{map|reduce}.java.opts 给
child-jvm 指定额外的设置项和在Job中设置参数(如:运行时连接器(
run-time linker ) 用不标准路径(通过
-Djava.library.path=<> 来定义,如果定义中包括
@taskid@ 标志,那么它将被修改为MapReduce task的
taskid ),去搜索共享库。)
下面的例子是一个具有多参数和替换的例子,展示了JVM的GC(垃圾回收)日志,并启动一个JVM JMK的代理密码,以便它能连接Jconsole,以查看clild的内存,线程和获取线程垃圾。还设置了map和reduce child JVM 的最大
heap-size(堆)的大小分别为512MB和1024MB。还为child-jvm添加一个额外的路径:
java.library.path 。(mapreduce-site.xml文件)
<property>
<name>mapreduce.map.java.opts</name>
<value>
-Xmx512M -Djava.library.path=/home/mycompany/lib -verbose:gc -Xloggc:/tmp/@ [email protected]
-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
</value>
</property>
<property>
<name>mapreduce.reduce.java.opts</name>
<value>
-Xmx1024M -Djava.library.path=/home/mycompany/lib -verbose:gc -Xloggc:/tmp/@ [email protected]
-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
</value>
</property>
|
十一、内存管理:
用户或管理员可以指定child-task启动的最大虚拟内存,和任何su-process(子进程)启动递归,用
mapreduce.{map|reduce}.memory.mb 进行设置,请注意,这里设置的值,是对每个进程的限制。
mapreduce.{map|reduce}.memory.mb 应该指定为MB。还有,它的值必须大于等于Jvm的
-Xmx 值,否则VM有可能无法启动。
注:
mapreduce.{map|reduce}.java.opts 仅用于对从
MRAppMaster 启动的
child tasks 的
配置。而守护进行的内存配置选项,是记录在Hadoop守护进程的环境配置。
框架的某些部件的有效内存也可以配置,在map和reduce任务中,通过调整参数,影响并发操作和磁盘的数据命中率,从而影响整体性能。对文件系统的作业的Counters(总计汇报设备)进行详细监测,特别是对于从map到reduce的 byte counts(字节统计)的详细监测, 这种监测对于调整参数是非常有用的。
十二、Map的相关参数:
由map产生的
记录,将被序列化后,写入
buffer
,
metadata
则将被写入到
accounting buffers
。如下列选项所描述,当序列化缓冲区或元数据超过一个阈值时,缓冲区中的内容将被分类并写入到磁盘中,同时map继续输出记录。如果在溢出过程中完全填充缓冲区,则该map线程将阻塞。当map执行完毕后,任何剩余的记录都将被写入到磁盘,所有的磁盘片段被合并为一个单独的文件。把溢出到磁盘的数量减到最小,可以减少map的执行时间,但是,更大的缓冲区同样也减少了mapper的可用内存。
参数名称
|
参数类型
|
描述
|
mapreduce.task.io.sort.mb
|
int
|
map产生的序列化,总计缓存的存储记录的累计大小,以MB为单位。 |
mapreduce.map.sort.spill.percent
|
float
|
序列化缓冲区中的软限制。一旦达到,线程将开台将内容溢出到后台磁盘中。
|
其他说明:
如果超过溢出阈值或者正在溢出了,收集器将继续工作,直到溢出结束。例如,如果
mapreduce.map.sort.spill.percent
设置为0.33,当溢出开始,缓冲区的剩余部分将被填满,接下来,溢出将包括所有已收集到的记录;或者缓冲区设置为0.66,将不会产生额外的溢出。换句话说,阈值是定义触发器,而不是阻塞。
大于序列化缓冲区的记录将首先触发溢出,然后被溢出到一个单独的文件。这是因为,它不明确这个记录是否第一时间传递给combiner中。
十三、Shuffle、Reduce的相关参数:
如前面所述,每个reduce通过内存中Http,获取由分区器(Partitioner)指定的output,定期的将这些output进行合并,然后写入到磁盘中。如果开启了对map的output进行中间记录压缩,每个Output被解压到内存中。下列的设置项,对在reduce之前,将outputs合并到磁盘中的频率产生影响,以及对map output 到 reduce间的内存
分配产生影响。
参数名称
|
参数类型
|
描述
|
mapreduce.task.io.soft.factor |
int |
指定同一时间,合并磁盘上分段的数量。在合并执行期间,它限制了打开文件的数量及压缩解码器的数量。如果文件的数量超过这个限制,合并器将在几个途径中继续。虽然这限制同样适用于map,但大多数jobs都应该配置,以使这个限制不可能达到。 |
mapreduce.reduce.merge.inmem.thresholds
|
int
|
在map outputs合并到磁盘前,提取map outputs分类到内存中的数量。像前面说的溢出阈值一样,不是定义一个分区,而是定义一个触发器。在实践中,这通常会设置得比较高(默认值1000)或者禁用(设置为0),因为在内存中合并比在磁盘中合并的代价高得多(详见本表下面的说明)。这个设置的阈值,仅对于shuffle过程中的合并频率产生影响。
|
mapreduce.reduce.shuffle.merge.percent
|
float
|
在内存中开始合并map outputs之前,取出map outputs的内存阈值,表示为分配给map outputs分类的内存在内存中的百分比。因为,map outputs不适合在内存中滞留,因此,设置较高的阈值,可能会降低获取和合并的并行操作。相反,高达1的阈值,对reduces的内存输入更加有效。
这个设置的阈值,仅对于shuffle过程中的合并频率产生影响。
|
mapreduce.reduce.shuffle.input.buffer.percent
|
float
|
内存百分比,相对于在 mapreduce.reduce.java.opts中指定的最大的块大小,在shuffle过程中,能分配给map outputs的分组内存。虽然应该为框架预留一些内存,一般来说,该参数设置得足够高,去存储大量数据和大量的map outputs是有利的。 |
mapreduce.reduce.input.buffer.percent
|
float
|
内存百分比,
相对于map outputs到reduce之间最大的块大小。当reduce开始运行时, map outputs将被合并到磁盘,直到这些记录在这个定义的资源限制以下为止。默认情况下,在reduce开始运行之前,所有的map outputs都将合并到磁盘,这样可以使reduce有最大的可用内存。内存密集型的reduces,应该调大这个值,避免读写磁盘。
|
其他说明:
- 如果一个map output大于分配给复制所有map outputs的内存25%以上,那么,它将直接被写入磁盘,而不需要经过内存。
- Combiner运行时,关于高合并阈值和大缓存区的推理可能不成立。所有map outputs被取出之前,合并就已经开始了,Combiner当溢出到磁盘的时候,才开始运行。在某些情况下,通过占用资源的合并map outputs 而制造出小的溢出,好过去增大缓冲区的大小,更能达到最优reduce时间。
- 当合并开始,内存中的map outputs写到磁盘同时开始reduce运行,如果中间的合并是必须的,因为存在溢出段和mapreduce.task.io.sort.factor定义的段至少会被写入磁盘,在内存中的map outputs将被合并到中间数据集中。
十四、参数配置:
下列属性被定位于每个任务运行的作业配置。
参数名称
|
参数类型
|
描述
|
mapreduce.job.id |
String |
作业的id |
mapreduce.job.jar
|
String
|
job.jar的工作目录
|
mapreduce.job.local.dir
|
String
|
job指定的共享活动空间
|
mapreduce.task.id
|
String
|
任务id |
mapreduce.task.attempt.id
|
String
|
尝试任务id |
mapreduce.task.is.map
|
boolean
|
该任务是否为map任务
|
mapreduce.task.partition
|
int
|
作业中,任务的id |
mapreduce.map.input.file
|
String
|
map方法正在读取的文件名
|
mapreduce.map.input.start
|
long
|
map分割input的偏移量
|
mapreduce.map.input.length
|
long
|
map分割input的字节数
|
mapreduce.task.output.dir
|
String
|
任务的临时输出目录
|
说明:
一个流作业(streaming job)执行过程中,名称中有“mapreduce”的参数会被改变。点(.)会被变成下划线(_)。比如:mapreduce.job.id 会变成 mapreduce_job_id ,还有 mapreduce.job.jar 会变成 mapreduce_job_jar 。在流作业的mapper或reducer中,要通过带有下划线的参数名才能获取到值。
十五、Task的日志:
task的标准输出、错误输出和系统日志,可以通过NodeManager 进行读取和直接写入到
${HADOOP_LOG_DIR}/userlogs 路径下。
十六、Distributing Libraries(分布式库):
DistributedCache 也能被分布在jars和本地库之间,用到map或reduce任务中。child-jvm总是有自己的当前工作目录,这目录被添加到 java.library.path and LD_LIBRARY_PATH 。因此,缓存库可以通过
system.loadlibrary 或
system.load 进行加载。如何通过分布式缓存加载共享库的详细信息,被记录在:
Native Libraries 。
十七、作业的提交及监测:
Job是用户作业与
ResourceManager (资源管理器)主要接口。Job提供一个装置,用来提交作业,追踪作业执行情况,使用组件进行报告和写日志,获取MapReduce客户端机器的状态信息等等。
作业提交过程包括:
- 检查Job指定的输入和输出格式;
- 运算Job的InputSplit的值;
- 必要时,为Job的 DistributedCache 设置必须的统计信息;
- 复制Job的jar包到文件系统中MapReduce系统目录中;
- 提交作业给 ResourceManager 和随机监测它的状态。
Job的历史文件也会被记录在用户指定的目录:mapreduce.jobhistory.intermediate-done-dir 和 mapreduce.jobhistory.done-dir ,作为Job的默认输出目录。
用户可以用下面的命令行查看指定目录中的历史日志的概要信息:(
未试成功)
$ mapred job -history output.jhist
该命令将打印作业的详情,作业失败和死亡的提示详情。
作业的更多详情,如执行成功的任务和为每个任务的尝试,可以通过下面的命令来查看:(
未试成功)
$ mapred job -history all output.jhist
一般情况下,用户利用Job来创建应用程序,描述Job的各个方面,提交Job,并监测其执行情况。
十八、Job Control:
用户可能需要连接多个MapReduce Job 一起来完成一系列复杂的任务。这是相当容易的,因为Job的输出一般都存储在分布式文件系统中,反过来说,Job的output可以作下个任务的输入。
那么,这也意见着,每个客户机器,都需要确保Job已经执行完毕(不管是成功或者失败)。作业的控制方式有如下两种:
- Job.submit() : 将作业提交给集群,并立即返回。
- Job.waitForCompletion(boolean) : 将作业提交给集群,并等待作业完成。
十九、Job Input:
InputFormat 描述了一个MapReduce作业的输入规范。MapReduce框架依赖于InputFormat进行工作:
- 校验输入是否规范;
- 将输入文件进行逐一的拆分成,拆分成符合框架逻辑的InputSplit实例,然后每一个InputSplit实例都被分配到一个独立的Mapper。
- RecordReader 实现了从符合框架逻辑的InputSplit实例收集输入的记录,提供给Mapper进行处理。
InputFormat的默认实现,通常是FileInputFormat的子类,对输入的文件(以byte为单位)进行分割,分割为符合框架逻辑的InputSplit实例。以文件系统的block大小,作为输入文件的分割界线。如果分隔线以下的文件,要进行分割,则可以通过mapreduce.input.fileinputformat.split.minsize 进行设置。
显然,遵守记录边界,基于输入文件的大小进行逻辑分割,是不以支持许多应用程序的。在这种情况下,应用程序应该实现RecordReader ,负责关联记录边界和提出一个面向记录逻辑分割的视图给每个任务。
TextInputFormat是默认的InputFormat。
如果Job的InputFormat是由TextInputFormat提供的,框架检查文件是.gz的扩展名文件,则会自动使用合适的CompressionCodec,对文件进行自动解压。然而,需要特别指明的是,.gz的压缩文件,是不能进行分割的,每个压缩文件只能由一个Mapper进行处理。
19.1 InputSplit:
InputSplit代表一个Mapper对数据进行处理。
通常情况下,InputSplit提供了一个面向字节的输入,负责对RecordReader 进行处理,并提出一个面向记录的视图。
FileSplit是默认的InputSplit。它把输入的文件路径设置到mapreduce.map.input.file ,用于进行逻辑分割。
19.2 RecordReader:
RecordReader从一个InputSplit中读取<key,value>。
通常RecordReader把input转换成面向字节的输入,然后提供给InputSplit,再提供一个面向记录的输入给Mapper实例进行处理。从而RecordReader承担起从记录中提取键值对的任务。
二十、Job Output:
OutputFormat 描述了MapReduce作业的输出规范。
框架依赖OutputFormat进行工作的格式为:
- 输出规范性校验;例如:检查输出目录是否存在。(输出目录不能存在)
- 提供RecordWriter实例,将Job的结果写到outputs文件中,并存放到分布式文件系统中;
TextOutputFormat是默认的 OutputFormat.
20.1 OutputCommitter:
OutputCommitter描述一个已提交的MapReduce作业的任务输出。
MapReduce框架job依赖的OutputCommitter,可以用来:
- 在初始化过程中,设置Job。例如,在job初始化过程中,为Job创建临时输出目录。当Job处于预备(PREP)状态和所有的task都初始化完毕后,通过一个单独的task来配置Job。一旦配置job的task执行完成,则Job的状态会变为运行中(RUNNING)。
- Job执行完毕,清理Job。例如,Job执行完毕后,删除掉为Job创建的临时输出目录。Job清理是由一个单独的task来执行的,在job结束后被执行。Job宣告执行完毕的状态有(SUCCEDED,FAILED,KILLED),然后完成Job的清理工作。
- 设置task的临时输出。task初始化期间,task配置将作为相同任务的一部分,在初始化过程中完成task设置。
- 检查task是否需要进行提交。这样是为了避免无用的提交。如果一个task不需要提交,那 就不提交。
- 提交task的输出。task一旦完成,如果有输出的,那么task会提交输出。
- 放弃提交任务。如果task被标志为:failed,killed,那么task的输出将被清理掉。如果task清除失败(发生异常块),那么将会启动一个单独的task(具有相同的尝试ID),继续完成task的清理工作。
FileOutputCommitter 是默认的 OutputCommitter。在NodeManager中,Job的设置和清除都会占用map或reduce的容器,不管是哪一个都是有用的。JobCleanup task TaskCleanup task JobSetup task 拥有最高的优先级,他们三个的优化级,按照该顺序。
20.2 Task Side-Effect(副作用) Files :
一些应用程序,任务组件需要创建或写入数据到站点中的目录,而这个目录与最终job的输出目录又一样。两个相同实例的Mapper,Reducer同时运行(例如:推测作业),它们可能打开或写入分布式文件系统中的相同目录,在这种情况下可能会出现问题。因此,应用程序不得不为每一个尝试作业定义一个唯一的名称(
使用attemptid,则表示为: attempt_200709221812_0001_m_ 000000 _0
),而不是为每个作业。
框架为了避免这种问题,当OutputCommitter是FileOutputCommitter的时候,保持一个特殊的输出${mapreduce.output.fileoutputformat.outputdir}/_temporary/_${taskid},子目录可以通过${mapreduce.task.output.dir}为每个尝试作业在文件系统中建立其对应的输出目录。在成功完成尝试任务后 ,在${mapreduce.output.fileoutputformat.outputdir}/_temporary/_${taskid}中的文件将被移到${mapreduce.output.fileoutputformat.outputdir}目录中。当然,框架会丢弃掉执行失败的尝试作业其对应目录中的文件,这个过程对于应用程序是完全透明的。
在task执行过程中,通过FileOutputFormat.getWorkOutputPath(Conext),应用程序的application-writer,能够利用这个特性,在${mapreduce.task.output.dir} 目录下面,创建任务需要的任何side-files。框架将促使他们完成尝试任务,从而消除需要为每个尝试任务指定一个唯一目录的需要。
注意事项:
特定的尝试任务执行期间的${mapreduce.task.output.dir}的值,事实上是${mapreduce.output.fileoutputformat.outputdir}/_temporary/_{$taskid}值,这个值是由框架来设置的。所以,刚刚通过FileOutputFormat.getWorkOutputPath(Conext)返回目录来创建的任何一个side-files,都是从MapReduce 作业利用这一特性而得来的。
整个讨论过程,适用于没有Reducer的Maps的输出,在这情况下,将直接写入到HDFS中。
18.3 RecordWriter:
RecordWriter写入<key,value>键值对到输出文件中。
RecordWriter实现将Job的导出写到文件系统中。
二十一、Other Useful Features(其他有用的特性):
21.1 Submitting Jobs to Queues (提交Job到队列中):
用户可以把Job提交到队列(Queues)中。Queues,是一个Job集合,允许系统提供特别的功能。例如,Queues用ACLs来控制,哪些用户可以向它提交作业。Queues期待被作为Hadoop的主要调度器。Hadoop配置了一个叫“default”的强制queue。Queue的名称定义在mapreduce.job.queuename 直接下层property 节点中(Hadoop site configuration文件中)。有些作业调度器支持多个Queues,如Capacity Scheduler(容量调度器)。
一个Job要定义queue,它通过mapreduce.job.queuename属性来提交,或者通过Configuration.set(MRJobConfig.QUEUE_NAME, String)应用程序接口进行提交。设置Queue的名称是可选的。如果一个作业被提交时没有指定queue名称,那么该Job将被提交到“default”queue。
21.2 Counters :
Counters 代表全局的计数器,由MapReduce框架或应用程序自行定义。每个Counter可以是任何的枚举类型。某个枚举类型的都在Counters.Group这个类型组中。在map、reduce方法中,应用程序可以通过Counters.incrCounter(Enum, long) 或者Counters.incrCounter(String, String, long) 来定义任意计数器(任意枚举型的计数器)和更新计数器。这些Counter作为全局计数器被框架进行汇总。
21.3 DistributedCache :
DistributedCache 使分发应用程序指定的,大的,只读的文件更有效。
DistributedCache 是一个由MapReduce框架提供的设备,用于缓存应用程序需要的文件(text,archives,jars等等)。
在Job中,应用程序可以通过Url(hdfs://)指定需要被缓存的文件。DistributedCache 会假定该文件已存在文件系统中。在Job任务在节点上运行之前,框架将会把task所需的文件复制到该slave节点中。它的功效源于一个事实,在slave节点中,每个job只能复制一次文件,它能缓存未归档的文件。DistributedCache会跟踪文件修改的时间
戳。显示,在Job运行过程中,缓存文件不应该被应用程序及外部修改。
DistributedCache能够被用于分发简单的,只读的数据或者文本文件和其他复杂类型的文件,如:archives,jars等等。在slave节点中,archives(zip,tar,tgz,tar.gz文件)都是未归档文件。文件具有执行权限集。
通过设置mapreduce.job.cache.{files|archives}属性,files或者archives能进行分布缓存。如果有多个文件或者归档文件想进行分布缓存,可能通过“,”逗号分隔字符串路径来指定。也可以通过应用程序接口: Job.addCacheFile(URI)/ Job.addCacheArchive(URI) and Job.setCacheFiles(URI[])/ Job.setCacheArchives(URI[]) 来设置,这里面的URI为 hdfs://host:port/absolute-path#link-name。在流作业中,还可以通过命令行:-cacheFile/-cacheArchive来设置。
DistributedCache也可以作为一种基本的软件分发机制,用于map、reduce作业中。它能被用于jar包和本地库。Job.addArchiveToClassPath(Path) 、Job.addFileToClassPath(Path) 应用程序接口,能用于缓存文件或者Jar包,也可以向child-jvm的classpath 添加。同样也可以通过配置文件 mapreduce.job.classpath.{files|archives}实现。同样的缓存文件,被系统连接到task的工作目录,能被用于分布本地库和加载。
23.3.1 Private and Public DistributedCache Files:
DistributedCache文件可以是公有的,也可以是私有的,这决定了这些文件如何在slave节点间共享。
- 私有的:DistributedCache文件被缓存在本地目录中,供私人的Job使用。这些文件共享仅给指定用户的所有的task和jobs,并不能供slaves上的其他用户使用。借用文件系统的权限来把文件变成私有的文件,这个文件系统一般为HDFS。
- 公有的:DistributedCache文件被缓存在全局的目录中,文件对于所有用户都是公开的,可见的。这些文件能供slaves上所有用户的task,job使用。借用文件系统的权限来把文件变成公有的文件,这个文件系统一般为HDFS。如果用户打算使一个文件变为公有的,那么文件权限设置为word readable,而且目录必须设置为executable。
23.4 Profiling(分析器):
分析器一个通用的程序,以获取一个有代表性的(2或3个)样本,为maps和reduces的样本而内置的java工具。用户可以指定系统是否需要为一些任务收集探测信息,可以通过配置文件属性来指定:mapreduce.task.profile。这个值也可以通过应用程序API来设置:Configuration.set(MRJobConfig.TASK_PROFILE, boolean)。如果设置为ture,作业启用分析器,探查信息存储在用户日志目录中。默认情况下,作业不启用分析器。
一旦用户启用分析器,就可以通过mapreduce.task.profile.{maps|reduces}属性,来设置分析器的分析范围。这个值也可以通过应用程序API来设置:Configuration.set(MRJobConfig.NUM_{MAP|REDUCE}_PROFILES, String)。
默认情况下,指定的范围是0-2。
用户可以指定分析器的参数,通过配置属性:mapreduce.task.profile.params。这个值也可以通过应用程序API来设置:Configuration.set(MRJobConfig.TASK_PROFILE_PARAMS, String)。如果该字符串包含"
%s",则将在任务运行时替换分析输出文件的名称。这些参数将被传递给task child JV 命令行上。分析器参数的默认值是:-agentlib:hprof=cpu=samples,heap=sites,force=n,thread=y,verbose=n,file=%s。
23.5 Debugging:
MapReduce框架提供一个设备用于运行用户提供调试的脚本。以处理任务日志为例,当一个task运行失败时,用户可以运行一下调试脚本。脚本是获得任务的stdout和stderr输出,系统日志和jobconf。从调试脚本的stdout和stderr输出显示在控制台诊断器上,也被视为Job的UI的一部分。下面的章节,接讨论用户如何提交一个调试脚本。脚本文件需要分发并提交给框架。
如何分发脚本文件:
用户需要使用distributedcache分发和使用脚本文件。
如何提交脚本:
提交调试脚本的快速方法是为属性设置值:mapreduce.map.debug.script 和 mapreduce.reduce.debug.script ,分别用于调试map和reduce。这些属性也可以通过应用程序接口来设置:Configuration.set(MRJobConfig.MAP_DEBUG_SCRIPT, String) 和Configuration.set(MRJobConfig.REDUCE_DEBUG_SCRIPT, String)。如果在流工作中,也可以通过命令行来设置:-mapdebug 和 -reducedebug 。
脚本的参数是任务的stdout,stderr,syslog和jobconf文件。
调试命令,运行在MapReduce任务失败的节点,是:
$script $stdout $stderr $syslog $jobconf
23.6 Data Compression(数据压缩):
Hadoop MapReduce为application-writer提供了一个为map-outputs和job-outputs指定压缩算法的设备。框架还附带了CompressionCodec实现的一个zlib压缩算法。gzip, bzip2, snappy, and lz4 格式的文件都支持。
Intermediate Outputs:
通过应用程序API:Configuration.set(MRJobConfig.MAP_OUTPUT_COMPRESS, boolean),应用程序可以控制对map-outputs进行压缩,通过应用程序API:Configuration.set(MRJobConfig.MAP_OUTPUT_COMPRESS_CODEC, Class) ,启用压缩算法CompressionCodec。
Job Outputs:
通过应用程序API:FileOutputFormat.setCompressOutput(Job, boolean),应用程序可以控制对 job-outputs进行压缩。通过应用程序API:FileOutputFormat.setOutputCompressorClass(Job, Class),可以启用压缩算法:
CompressionCodec。
如果Job-outputs存储在 SequenceFileOutputFormat中,则需要通过应用程序API:SequenceFileOutputFormat.setOutputCompressionType(Job, SequenceFile.CompressionType),指定SequenceFile.CompressionType (i.e. RECORD / BLOCK - defaults to RECORD)。
Skipping Bad Records(跳过不良计录):
Hadoop 提供一个设置项,明确的设置了不良的输入记录,当map处理输入记录时,这些不良的输入记录将被跳过。应用程序可以通过SkipBadRecords类,来控制这个特性。此特性可以用于在明确map任务遇到损坏的输入记录的时候。这通常是由于map方法中有bug所导致的。通常情况下,用户必须修复这些bug。然而,这些bug有时候是无法修复的。例如,有些bug存在于第三方代码库,第三方代码库的代码是无法修改的。在这种情况下,task失败是必须的,即使是多次的尝试,job最终还是失败。用了这个特性,仅仅是丢弃一小部分的不良数据,对一个应用程序来讲是可以忽略的(对非常大的数据进行统计的情况下)。
默认情况下该特性是开启的。要开启,需要指定: SkipBadRecords.setMapperMaxSkipRecords(Configuration, long) 和 SkipBadRecords.setReducerMaxSkipGroups(Configuration, long)。启用这个特性,框架会在一定数量的map task失败后,进入“跳跃模式”。更多的详情见:SkipBadRecords.setAttemptsToStartSkipping(Configuration, int)。在“跳跃模式”下,map任务,将保持对其范围内的数据的处理。要做到这一点,该框架要依靠Counter(计数器)的处理结果。可以查看:SkipBadRecords.COUNTER_MAP_PROCESSED_RECORDS 和SkipBadRecords.COUNTER_REDUCE_PROCESSED_GROUPS 了解更多详情。这个Counter可以令框架知道有多少记录处理成功,多少范围的记录处理失败。再进一步尝试后,这些范围的记录就会被丢弃。
被丢弃的记录的数量依据应用程序的Counter频繁的处理记录。推荐,Counter在每个记录被处理完成后,再进行增加计数。在一些批处理应用程序中,有可能不会出现这种情况。在这种情况下,框架可以跳过
不良记录周围的记录。用户可控制跳过记录的数量:SkipBadRecords.setMapperMaxSkipRecords(Configuration, long) 和SkipBadRecords.setReducerMaxSkipGroups(Configuration, long) 设置。框架将通过折半搜索,尝试缩小跳过的记录的数量。跳过过的记录被分为两半,先执行一半。在失败后,框架就能知道哪一半包含不良记录。这个任务将被重复执行,直到不良记录被找到或所有
尝试任务都耗尽。想要增加尝试任务的数量,可以设置: Job.setMaxMapAttempts(int) and Job.setMaxReduceAttempts(int)。
跳过的文件会被写到HDFS中的序列格式文件中,以便后来进行分析。存储位置可以设置:SkipBadRecords.setSkipOutputPath(JobConf, Path)。
23.7 实例 WordCount v2.0:
这个例子,应用了到目前为止,我们讨论过的框架的很多个特性。首先,HDFS需要启动,处于运行中。特别是对distributedcache相关特征。因此,它只适用于伪分布式或完全分布式的Hadoop的环境。