概述
FairScheduler可以通过配置yarn.scheduler.fair.preemption参数为true,开启抢占式调度,默认为false,即不开启。
FairScheduler在计算这个队列允许抢占其它队列的资源大小时,如果这个队列使用的资源低于其minshare的时间超过了抢占超时时间,那么,应该抢占的资源量就在它当前的fair share和它的min share之间的差额。如果队列资源已经低于它的fair share的时间超过了fairSharePreemptionTimeout,那么他应该进行抢占的资源就是满足其fair share的资源总量。如果两者都发生了,则抢占两个的较多者。
抢占式调度主要由2个线程配合完成:
- UpdateThread:计算队列和app的fairShare,然后计算app的fairshareStarvation量和app的minshareStarvation量,并记录这些处于starvation状态的app。
- FSPreemptionThread:处理处于starvation状态的app。基于给定的app,获取一批可用于抢占的cotainer,并在必要时kill掉这些container。
FSPreemptionThread
FairScheduler在启动服务时,会启动FSPreemptionThread。
如果开启抢占式调度,则会创建一个后台线程FSPreemptionThread 。
if (this.conf.getPreemptionEnabled()) {
createPreemptionThread();
}
FSPreemptionThread的run()方法逻辑
- FairScheduler的上下文FSContext的FSStarvedApps变量中维护了一个PriorityBlockingQueue<FSAppAttempt>队列,该队列中记录了处于starvation状态的app。FSPreemptedThread从队列中获取处于starvation状态的app 。
- 基于给定的app,抢占一定数量的container用来满足该app的需求。返回可用于抢占的container。
- 在时间超过waitTimeBeforeKill后,标记为待抢占的container仍没有被释放,则preemptContainers()方法主动kill掉该container。
public void run() {
while (!Thread.interrupted()) {
try {
//FairScheduler的上下文FSContext的FSStarvedApps变量中维护了一个PriorityBlockingQueue<FSAppAttempt>队列
//该队列中记录了处于starvation状态的app
//FSPreemptedThread从队列中获取处于starvation状态的app
FSAppAttempt starvedApp = context.getStarvedApps().take();
// Hold the scheduler readlock so this is not concurrent with the
// update thread.
schedulerReadLock.lock();
try {
//identifyContainersToPreempt()方法:基于给定的app,抢占一定数量的container用来满足该app的需求。返回可用于抢占的container。
//preemptContainers()方法:在时间超过waitTimeBeforeKill后,标记为待抢占的container仍没有被释放,则preemptContainers()方法主动kill掉该container。
preemptContainers(identifyContainersToPreempt(starvedApp));
} finally {
schedulerReadLock.unlock();
}
starvedApp.preemptionTriggered(delayBeforeNextStarvationCheck);
} catch (InterruptedException e) {
LOG.info("Preemption thread interrupted! Exiting.");
Thread.currentThread().interrupt();
}
}
}
preemptContainers()方法
如果这个container已经被标记为待抢占,并且距离标记时间已经超过了waitTimeBeforeKill却依然没有被自己的ApplicationMaster主动释放的container,那么直接杀死这个Container。
private void preemptContainers(List<RMContainer> containers) {
// Schedule timer task to kill containers
preemptionTimer.schedule(
new PreemptContainersTask(containers), warnTimeBeforeKill);
}
PreemptContainersTask
执行kill container事件
private class PreemptContainersTask extends TimerTask {
private final List<RMContainer> containers;
PreemptContainersTask(List<RMContainer> containers) {
this.containers = containers;
}
@Override
public void run() {
for (RMContainer container : containers) {
ContainerStatus status = SchedulerUtils.createPreemptedContainerStatus(
container.getContainerId(), SchedulerUtils.PREEMPTED_CONTAINER);
LOG.info("Killing container " + container);
scheduler.completedContainer(
container, status, RMContainerEventType.KILL);
}
}
}
UpdateThread
AbstractYarnScheduler在启动服务时,会启动UpdateThread。UpdateThread是AbstractYarnScheduler的内部类。
/**
* Thread which calls {@link #update()} every
* <code>updateInterval</code> milliseconds.
*/
private class UpdateThread extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
synchronized (updateThreadMonitor) {
updateThreadMonitor.wait(updateInterval);
}
update();
} catch