(八)Spark源码理解之DAGScheduler---part1

本文详细探讨了Spark中DAGScheduler的核心概念、关键变量及其内部工作流程,包括如何划分stage、生成DAG图、提交任务集以及与TaskScheduler的交互。通过解析DAGScheduler中的重要方法和变量,读者可以深入了解DAGScheduler如何高效地执行任务调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


在讲述DAGScheduler之前,先介绍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:通过initializeEventProcessActor()方法生成一个DAGSchedulerEventProcessActor类型的消息传递机制,用来通知DAGScheduler处理各种事件,如任务完成(taskStarted),任务集失败(TaskSetFailed),executor丢失等事件;

DAGScheduler的源码部分虽然有很多方法,看起来很混乱,但其实方法之间存在着相互依存,调用的关系,理清思路后就不会那么迷糊了,首先我画出DAGScheduler中的某部分的方法调用的框架,这部分实现了上面所提到的DAGScheduler如何划分stage以及如何向TaskScheduler提交任务集的功能;


我认为DAGScheduler划分stage的核心部分在于getMissingParentStages()这个函数,提交任务集给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中,若为宽依赖即ShuffleDependency则该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
  }
生成父stage的过程我将其描述为如下图所示:

未完待续。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值