DAGScheduler源码解析(一)

Spark 源码理解 DAGScheduler

DAGScheduler是Spark中比较重要的一部分,它属于高级调度,主要实现stage的划分,接着生成整个DAG图,以及如何为每个stage 生成任务集,并且将任务提交给TaskScheduler,基于这两点,我们对DAGScheduler的源码展开阅读,下面是DAGScheduler的简单构成图


DAGScheduler源码解析(一)_第1张图片

在讲述DAG之前,先介绍DAGScheduler中几个重要的变量:

jobIdToStageIds: HashMap容器,key值为job的id,value为job中所有的stage的id所组成的HashSet;

stageIdToJobIds: HashMap容器,key值为stage的id,value值为stage所属的job的id所组成的HashSet;
stageIdToStage: HashMap容器,key值为stage的id,value值为该stage所得到的Stage类(Stage类在之后会提到)
shuffleToMapStage: HashMap容器,key值为shuffle的id,value值为该shuffle操作Stage类,(Stage类在之后会提到)
eventProcessActor:通过initializeEvenProcessActor()方法生成一个DAGSchedulerEventProcessActor类型的消息传递机制,用来通知DAGScheduler处理各种事件,如任务完成(taskStarted),任务集失败(TaskSetFailed),executor丢失等事件
DAGScheduler的源码部分虽然有很多方法,看起来很混乱,但其实方法之间存在着相互依存,调用的关系,理清思路后就不会那么迷糊了,首先我画出DAGScheduler中的某部分的方法调用的框架,这部分实现了上面所提到的DAGScheduler如何划分stage以及如何向TaskScheduler提交任务集的功能
DAGScheduler源码解析(一)_第2张图片

我认为DAGScheduler划分stage的核心部分在于 getMissingParentStage() 这个函数,提交任务集给TaskScheduler的核心部分 在于submitMissingTasks()函数,大概框架就是这样,细节部分我们将在下面详细介绍,以划分stage和提交任务集为例

submitStage: 提交stage,即划分stage,它首先判断stage 的父stage 有没有被提交,如果父stage都被提交了,则提交这个stage 的任务集给TaskScheduler,否则对父stage进行submit操作。即判断父stage的父stage有没有提交,如果都提交了则提交这个父stage的任务集给TaskScheduler,否则对父stage的父stage进行submit操作,如此下去,这也体现了DAGScheduler调度stage的顺序是只有等父stage完成之后才能调度子stage

private def submitStage(stage: Stage) {
val jobId = activeJobForStage(stage)//查找含有该stage的所有的激活的job,因为一个stage可能存在在多个job中
if (jobId.isDefined) {//如果存在拥有该stage的激活的job,则进行下述操作
  logDebug("submitStage(" + stage + ")")
  if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
    val missing = getMissingParentStages(stage).sortBy(_.id)//得到stage的父stage,父stage可能有多个,因此将父stage进行排序
    logDebug("missing: " + missing)
    if (missing == Nil) {
      logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
      submitMissingTasks(stage, jobId.get) //如果stage没有父stage,则提交自己的任务集
      runningStages += stage//标志该stage为运行状态
    } else {
      for (parent <- missing) {//如果有父stage,则对父stage递归调用submitStage函数,进行submit操作
        submitStage(parent)
      }
      waitingStages += stage//处理完父stage后将该stage标志为等待状态,等待下次提交
    }
  }
} else {//如果该stage没有相应的激活的job,则丢弃该stage,丢弃的过程可以简述为首先找到拥有该stage的所有job,之后再找到这些job所拥有的所有stage,若其中有的stage已经提交任务集,则调用TaskSchedule人的cancelTasks取消掉提交的任务集
  abortStage(stage, "No active job for stage " + stage.id)
}

}

getMissingParentStages: 该方法得到stage的父stage,其实也就是实现DAGScheduler划分stage ,其主要就是依靠stage中的RDD的依赖关系,(而RDD的依赖关系又是根据RDD的各种操作即RDD类的各种方法生成的)进行划分,我们知道若stage遇到一个shuffle操作,则会生成一个新的stage,即stage是以shuffle开始的,这样也可理解为一个stage中最开始的那个RDD的依赖是ShuffleDependency,若某RDD为窄依赖则该RDD所依赖的父RDD还是在同一个stage中,若为宽依赖即ShuffleDepency则该RDD所依赖的父RDD就会在另一个stage中,我们即可根据这个父RDD找到该stage的父stage ,理解这个以后对于划分stage的认识会很有帮助的

private def getMissingParentStages(stage: Stage): List[Stage] = {
val missing = new HashSet[Stage]//建立存放该stage的父stage的HashSet容器
val visited = new HashSet[RDD[_]]//将已经访问过的RDD存放在HashSet容器中
def visit(rdd: RDD[_]) {//定义访问方法
  if (!visited(rdd)) {//如果RDD没有被访问过,则进行访问
    visited += rdd//添加RDD到已经访问过的RDD的容器中
    if (getCacheLocs(rdd).contains(Nil)) {
      for (dep <- rdd.dependencies) {//获取该RDD的依赖
        dep match {
          case shufDep: ShuffleDependency[_,_] =>//若为Shuffle依赖,则按照上述所说该RDD依赖的RDD所在的stage为父stage
            val mapStage = getShuffleMapStage(shufDep, stage.jobId)//生成父stage,具体的生成过程接下来讲述
            if (!mapStage.isAvailable) {//若该stage不存在,则添加到父stage容器中
              missing += mapStage
            }
          case narrowDep: NarrowDependency[_] =>//若为窄依赖,则继续访问父RDD
            visit(narrowDep.rdd)
        }
      }
    }
  }
}
visit(stage.rdd)//通过访问stage的RDD来得到该stage的父stage
missing.toList
  }

DAGScheduler源码解析(一)_第3张图片

你可能感兴趣的:(DAGScheduler源码解析(一))