Spark在设计上将DAGScheduler和TaskScheduler完全解耦合, 所以在资源管理和task调度上可以有更多的方案

现在支持, LocalSheduler, ClusterScheduler, MesosScheduler, YarnClusterScheduler

先分析ClusterScheduler, 即standalone的Spark集群上, 因为比较单纯不涉及其他的系统, 看看Spark的任务是如何被执行的

 

  private var taskScheduler: TaskScheduler = {
case SPARK_REGEX(sparkUrl) =>
val scheduler = new ClusterScheduler(this) // 创建ClusterScheduler
val backend = new SparkDeploySchedulerBackend(scheduler, this, sparkUrl, appName) // 创建SparkDeploySchedulerBackend
scheduler.initialize(backend)
scheduler
}

TaskScheduler接口, 注释写的非常清楚

/**
* Low-level task scheduler interface, implemented by both ClusterScheduler and LocalScheduler.
* These schedulers get sets of tasks submitted to them from the DAGScheduler for each stage,
* and are responsible for sending the tasks to the cluster, running them, retrying if there
* are failures, and mitigating stragglers. They return events to the DAGScheduler through
* the TaskSchedulerListener interface.
*/
private[spark] trait TaskScheduler {
def rootPool: Pool
def schedulingMode: SchedulingMode
def start(): Unit // 启动
def postStartHook() { }
def stop(): Unit
// Submit a sequence of tasks to run.
def submitTasks(taskSet: TaskSet): Unit // 核心, 提交taskset的接口
// Set a listener for upcalls. This is guaranteed to be set before submitTasks is called.
def setListener(listener: TaskSchedulerListener): Unit // TaskScheduler会使用这个listener来汇报当前task的运行状况,会注册DAGScheduler
// Get the default level of parallelism to use in the cluster, as a hint for sizing jobs.
def defaultParallelism(): Int
}

 

ClusterScheduler

对于集群的TaskScheduler实现, 相对于LocalScheduler

主要就是创建和管理schedulable tree, 参考Spark源码分析 – SchedulableBuilder

当然最终和cluster的executor通信还是需要依赖SparkDeploySchedulerBackend, 参考Spark源码分析 – SchedulerBackend

 

对于submitTasks,

首先将tasksetmanager放入schedulable tree等待schedule (delay schedule, 不一定会马上被调度到)

然后给SchedulerBackend发送reviveOffers event, 请求分配资源并launch tasks (launch的并一定是刚提交的tasks)

SchedulerBackend会向cluster申请workOffers(对于standalonebackend, 这步省略了), 然后再调用ClusterScheduler.resourceOffers来根据可用的workOffers分配tasks

最终给executors发送LaunchTask, 启动tasks

 

resourceOffers是核心函数, 当得到可用的workerOffer后, 用于从schedulable tree中schedule合适的被执行的tasks

resourceOffers的逻辑有点小复杂

1. 首先依次遍历sortedTaskSets, 并对于每个Taskset, 遍历TaskLocality

2. 越local越优先, 找不到(launchedTask为false)才会到下个locality级别

3. 在多次遍历offer list, 因为一次taskSet.resourceOffer只会占用一个core, 而不是一次用光所有的core, 这样有助于一个taskset中的task比较均匀的分布在workers上

4. 只有在该taskset, 该locality下, 对所有worker offer都找不到合适的task时, 才跳到下个locality级别

 

private[spark] class ClusterScheduler(val sc: SparkContext) extends TaskScheduler with Logging
{
var listener: TaskSchedulerListener = null
var backend: SchedulerBackend = null
val mapOutputTracker = SparkEnv.get.mapOutputTracker
var schedulableBuilder: SchedulableBuilder = null
var rootPool: Pool = null
// default scheduler is FIFO
val schedulingMode: SchedulingMode = SchedulingMode.withName(
System.getProperty("spark.scheduler.mode", "FIFO"))
  def initialize(context: SchedulerBackend) {
backend = context // 初始化SchedulerBackend
// temporarily set rootPool name to empty
rootPool = new Pool("", schedulingMode, 0, 0) // 创建Schedulable tree的root pool
schedulableBuilder = { // 用schedulableBuilder初始化Schedulable tree
schedulingMode match {
case SchedulingMode.FIFO =>
new FIFOSchedulableBuilder(rootPool)
case SchedulingMode.FAIR =>
new FairSchedulableBuilder(rootPool)
}
}
schedulableBuilder.buildPools()
}
  override def start() {
backend.start() // 启动SchedulerBackend
} override def submitTasks(taskSet: TaskSet) {
val tasks = taskSet.tasks
logInfo("Adding task set " + taskSet.id + " with " + tasks.length + " tasks")
this.synchronized {
val manager = new ClusterTaskSetManager(this, taskSet)
activeTaskSets(taskSet.id) = manager
schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties) // 将TaskSetManager加到Schedulable tree等待被调度执行
taskSetTaskIds(taskSet.id) = new util.HashSet[Long]()
backend.reviveOffers() // 调用SchedulerBackend的reviveOffers, 其实就是往DriverActor发送reviveOffers事件
}
 
  /**
* Called by cluster manager to offer resources on slaves. We respond by asking our active task
* sets for tasks in order of priority. We fill each node with tasks in a round-robin manner so
* that tasks are balanced across the cluster.
*/
  // 根据当前可用的worker offers, 分配tasks
def resourceOffers(offers: Seq[WorkerOffer]): Seq[Seq[TaskDescription]] = synchronized {
SparkEnv.set(sc.env) // Build a list of tasks to assign to each worker
val tasks = offers.map(o => new ArrayBuffer[TaskDescription](o.cores)) // 每个core可以分配一个task,所以对每个offer生成length为cores数目的ArrayBuffer
val availableCpus = offers.map(o => o.cores).toArray // 每个work可用的core数目的array
val sortedTaskSets = rootPool.getSortedTaskSetQueue() // 得到根据schedule算法排序后的TaskSetManager列表
// Take each TaskSet in our scheduling order, and then offer it each node in increasing order
// of locality levels so that it gets a chance to launch local tasks on all of them.
var launchedTask = false
for (taskSet <- sortedTaskSets; maxLocality <- TaskLocality.values) { // 嵌套, 遍历sortedTaskSets, 并对每个taskSet遍历所有TaskLocality
do {
launchedTask = false
for (i <- 0 until offers.size) { // 遍历每个offer, 试图在当前的taskset和当前的locality上找到合适的task
val execId = offers(i).executorId
val host = offers(i).host
for (task <- taskSet.resourceOffer(execId, host, availableCpus(i), maxLocality)) { // 每次只会返回最多一个task
tasks(i) += task
val tid = task.taskId
taskIdToTaskSetId(tid) = taskSet.taskSet.id
taskSetTaskIds(taskSet.taskSet.id) += tid
taskIdToExecutorId(tid) = execId
activeExecutorIds += execId
executorsByHost(host) += execId
availableCpus(i) –= 1 // 分配一个task, 所以availableCpus - 1
launchedTask = true
}
}
} while (launchedTask) // 找到,就继续在这个locality上找task, 否则放宽到下个locality,或下个taskset
} if (tasks.size > 0) {
hasLaunchedTask = true
}
return tasks
}
}

Spark源码分析 -- TaskScheduler的更多相关文章

  1. Spark源码分析 – 汇总索引

    http://jerryshao.me/categories.html#architecture-ref http://blog.csdn.net/pelick/article/details/172 ...

  2. Spark源码分析(三)-TaskScheduler创建

    原创文章,转载请注明: 转载自http://www.cnblogs.com/tovin/p/3879151.html 在SparkContext创建过程中会调用createTaskScheduler函 ...

  3. spark 源码分析之四 -- TaskScheduler的创建和启动过程

    在 spark 源码分析之二 -- SparkContext 的初始化过程 中,第 14 步 和 16 步分别描述了 TaskScheduler的 初始化 和 启动过程. 话分两头,先说 TaskSc ...

  4. Spark源码分析:多种部署方式之间的区别与联系(转)

    原文链接:Spark源码分析:多种部署方式之间的区别与联系(1) 从官方的文档我们可以知道,Spark的部署方式有很多种:local.Standalone.Mesos.YARN.....不同部署方式的 ...

  5. Spark 源码分析 -- task实际执行过程

    Spark源码分析 – SparkContext 中的例子, 只分析到sc.runJob 那么最终是怎么执行的? 通过DAGScheduler切分成Stage, 封装成taskset, 提交给Task ...

  6. Spark源码分析 – SchedulerBackend

    SchedulerBackend, 两个任务, 申请资源和task执行和管理 对于SparkDeploySchedulerBackend, 基于actor模式, 主要就是启动和管理两个actor De ...

  7. Spark源码分析 – DAGScheduler

    DAGScheduler的架构其实非常简单, 1. eventQueue, 所有需要DAGScheduler处理的事情都需要往eventQueue中发送event 2. eventLoop Threa ...

  8. Spark源码分析 – SparkContext

    Spark源码分析之-scheduler模块 这位写的非常好, 让我对Spark的源码分析, 变的轻松了许多 这里自己再梳理一遍 先看一个简单的spark操作, val sc = new SparkC ...

  9. Spark源码分析之七:Task运行(一)

    在Task调度相关的两篇文章<Spark源码分析之五:Task调度(一)>与<Spark源码分析之六:Task调度(二)>中,我们大致了解了Task调度相关的主要逻辑,并且在T ...

随机推荐

  1. floyd算法&迪杰斯特拉算法

    ; k<=n; k++) ; i<=n; i++) ; j<=n; j++) { gra[i][j]=min(gra[i][j],gra[i][k]+gra[k][j]); } vo ...

  2. 实例37foreach遍历数组

    package test; import java.util.List; import java.util.ArrayList; import java.util.Scanner; /** * @au ...

  3. NSURLErrorDomain Code=-999(转)

    原文:http://www.henishuo.com/nsurlerrordomain-code-999/ 前言 今天有一个线上bug,是分配给提供H5的团队的,但是后台查不出来原因.于是让前端iOS ...

  4. lucene 内存索引存储每个field里内容的相关代码

    相关的类调用关系 DocumentsWriterPerThread ——>DocFieldProcessor   DocumentsWriterPerThread里的consumer对象(类型是 ...

  5. Unix系统编程()发送信号kill

    与shell的kill命令类似,一个进程能够使用kill系统调用向另一进程发送信号. 之所以选择kill作为术语,因为早期UNIX实现中大多数信号的默认行为是终止进程. #include <si ...

  6. Unix domain socket IPC

    UNIX Domain socket 虽然网络socket也可用于同一台主机的进程间通讯(通过lo地址127.0.0.1),但是unix domain socket用于IPC更有效率:不需要经过网络协 ...

  7. TVS二极管的主要参数与选型

    TVS二极管的主要参数--转载 处理瞬时脉冲对器件损害的最好办法是将瞬时电流从敏感器件引开.TVS二极管在线路板上与被保护线路并联,当瞬时电压超过电路正常工作电压后,TVS二极管便发生雪崩,提供给瞬时 ...

  8. DRBD(Distributed Replicated Block Device) 分布式块设备复制 进行集群高可用方案

    DRBD是一个用软件实现的.无共享的.服务器之间镜像块设备内容的存储复制解决方案. 外文名 DRBD drbdadm 高级管理工具 drbdsetup 置装载进kernel的DRBD模块 drbdme ...

  9. Web app root system property already set to different value 错误原因及解决

    http://yzxqml.iteye.com/blog/1761540 ——————————————————————————————————————————————————————————————— ...

  10. Css三栏布局自适应实现几种方法

    Css三栏布局自适应实现几种方法 自适应实现方法我们可以从三个方法来做,一个是绝对定位 ,自身浮动法 和margin负值法了,下面我们一起来看看这三个例子吧,希望例子能帮助到各位同学. 绝对定位法三栏 ...