一、背景
当一个应用向YARN集群提交作业后,此作业的多个任务由于负载不均衡、资源分布不均等原因都会导致各个任务运行完成的时间不一致,甚至会出现一个任务明显慢于同一作业的其它任务的情况。如果对这种情况不加优化,最慢的任务最终会拖慢整个作业的整体执行进度。好在mapreduce框架提供了任务推断执行机制,当有必要时就启动一个备份任务。最终会采用备份任务和原任务中率先执行完的结果作为最终结果。如果推测执行被触发,则可以在输出的信息中看到Killed map/reduce tasks = {}。
二、Speculator
在MapReduce中,负责执行推测执行的接口规范为Sepculator,我们来看一下:
public interface Speculator
extends EventHandler<SpeculatorEvent> {
enum EventType {
ATTEMPT_STATUS_UPDATE,
ATTEMPT_START,
TASK_CONTAINER_NEED_UPDATE,
JOB_CREATE
}
// This will be implemented if we go to a model where the events are
// processed within the TaskAttempts' state transitions' code.
public void handleAttempt(TaskAttemptStatus status);
}
DefaultSpeculator是Speculator的默认实现。DefaultSpeculator负责处理SpeculatorEvent事件,目前主要包括四种事件,分别是:
JOB_CREATE:作业刚刚被创建时触发的事件,并处理一些初始化工作。
ATTEMPT_START:一个任务实例TaskAttemptImpl启动时触发的事件,DefaultSpeculator将会使用内部的推断估算器(默认是LegacyTaskRuntimeEstimator)开启对此任务实例的监控。
ATTEMPT_STATUS_UPDATE:当任务实例的状态更新时触发的事件,DefaultSpeculator将会更新推断估算器对任务的监控信息;更新正在运行中的任务(维护在runningTasks中);任务的统计信息(这些统计信息用于跟踪长时间未汇报心跳的任务,并积极主动的进行推断执行,而不是等待任务超时)
TASK_CONTAINER_NEED_UPDATE:任务Container数量发生变化时触发的事件。
Speculator的初始化
OK,接下来我们看下其是如何初始化的。
if (conf.getBoolean(MRJobConfig.MAP_SPECULATIVE, false) || conf.getBoolean(MRJobConfig.REDUCE_SPECULATIVE, false)) { //optional service to speculate on task attempts' progress speculator = createSpeculator(conf, context); addIfService(speculator); } speculatorEventDispatcher = new SpeculatorEventDispatcher(conf); dispatcher.register(Speculator.EventType.class, speculatorEventDispatcher);在MRAppMaster中的
serviceInit方法中,对其进行了初始化,步骤如下:当启用map任务推断(这里的MRJobConfig.MAP_SPECULATIVE实际由参数
mapreduce.map.speculative控制,默认是true)或者启用reduce任务推断(这里的MRJobConfig.REDUCE_SPECULATIVE实际由参数mapreduce.reduce.speculative控制,默认是true)时调用createSpeculator方法创建推断服务。最后将Speculator添加为MRAppMaster的子服务。向调度器dispatcher注册推断事件与推断事件的处理器SpeculatorEventDispatcher,以便触发了推断事件后交由SpeculatorEventDispatcher作进一步处理。
createSpeculator方法创建的推断服务的实现类默认是DefaultSpeculator,用户也可以通过参数yarn.app.mapreduce.am.job.speculator.class(即MRJobConfig.MR_AM_JOB_SPECULATOR)指定推断服务的实现类。其中的构造方法又通过反射创建了一个TaskRuntimeEstimator。
// "yarn.mapreduce.job.task.runtime.estimator.class"
Class<? extends TaskRuntimeEstimator> estimatorClass
= conf.getClass(MRJobConfig.MR_AM_TASK_ESTIMATOR,
LegacyTaskRuntimeEstimator.class,
TaskRuntimeEstimator.class);
Constructor<? extends TaskRuntimeEstimator> estimatorConstructor
= estimatorClass.getConstructor();
estimator = estimatorConstructor.newInstance();
estimator.contextualize(conf, context);