spark源码学习(五):stage的划分和task的创建

                               

                           spark源码学习(五):stage的划分和task的创建


      上一篇blog简单的阐述了和Resultstage和ActiveJob创建相关的源码,在这里我们接着上次没说完的submitStage,getMissingParentStages,submitMissingTasks两个函数开始.后面的这两个函数是在第一个函数里面调用的。

       submitStage的代码在上一篇已经贴出来啦,首先来看看那个getMissingParentStages是什么:

  private def getMissingParentStages(stage: Stage): List[Stage] = {
    val missing = new HashSet[Stage]//保存没有被访问过的Stage
    val visited = new HashSet[RDD[_]]//保存已经访问过的RDD
    //手动的维护栈就是为了防止内存泄漏
    val waitingForVisit = new Stack[RDD[_]]
    def visit(rdd: RDD[_]) {
      if (!visited(rdd)) {
        visited += rdd
        if (getCacheLocs(rdd).contains(Nil)) {//会缓存RDD到内存去,会加快访问速度
          for (dep <- rdd.dependencies) {
            dep match {
              case shufDep: ShuffleDependency[_, _, _] =>
                val mapStage = getShuffleMapStage(shufDep, stage.jobId)
                if (!mapStage.isAvailable) {
                  missing += mapStage
                }
              case narrowDep: NarrowDependency[_] =>
                waitingForVisit.push(narrowDep.rdd)
            }
}
        }
      }
    }
    waitingForVisit.push(stage.rdd)
    while (waitingForVisit.nonEmpty) {
      visit(waitingForVisit.pop())//递归的调用visit函数
    }
    missing.toList
  }
  
       上面的函数中有一个getCacheLocs函数的使用,主要作用就是把当前的RDD进行cache放到内存来加快访问的速度。具体操作就是如果rdd已经cache就返回cache的位置,否则就执行cache操作:


       为了更加明白上面的那个函数,我们来看看这个变量cacheLocs吧,源码中的注释还挺详细的:


       说的很明白,id就是所对应RDD的id,而values对应的是已经缓存之后每个rdd的partitions分别存放的位置。

       (1)函数后面的contains(Nil)也就是为了容错处理吧,因为完整的队列后面都是以Nil结尾的嘛。在得到RDD的父依赖之后,依据依赖类型的不同对应不同的操作。如果是shuffle依赖就会得到一个新的stage叫做shuffleMapStage,如下:


       最核心的 newOrUsedShuffleStage就是用来创建一个新的shuffleMapStage的,具体的就不去深究啦。

       (2)如果是窄依赖的话,就把当前的RDD放到waitingForVist的栈中,之后每次都会pop得到栈顶的RDD在进行同样的模式匹配工作,知道访问到最前面的那个RDD为止(当前stage的最前面的那个RDD)。我们会发现和stage相关的代码都是相同的code架构,对吧

说到这里,上面的getMissingParentStages就介绍完毕,下面继续来看看submitMissingTasks:这是重头戏

/** Called when stage's parents are available and we can now do its task. */
  private def submitMissingTasks(stage: Stage, jobId: Int) {
    logDebug("submitMissingTasks(" + stage + ")")
    // Get our pending tasks and remember them in our pendingTasks entry
    stage.pendingTasks.clear()


    // First figure out the indexes of partition ids to compute.
    val partitionsToCompute: Seq[Int] = {
      stage match {
        case stage: ShuffleMapStage =>
          (0 until stage.numPartitions).filter(id => stage.outputLocs(id).isEmpty)
        case stage: ResultStage =>
          val job = stage.resultOfJob.get
          (0 until job.numPartitions).filter(id => !job.finished(id))
      }
    }
    ....省略
    val properties = jobIdToActiveJob.get(stage.jobId).map(_.properties).orNull

    runningStages += stage

    var taskBinary: Broadcast[Array[Byte]] = null
    try {//不同的依赖不同的序列化机制
      // For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).
      // For ResultTask, serialize and broadcast (rdd, func).
      val taskBinaryBytes: Array[Byte] = stage match {
        case stage: ShuffleMapStage =>
          closureSerializer.serialize((stage.rdd, stage.shuffleDep): AnyRef).array()
        case stage: ResultStage =>
          closureSerializer.serialize((stage.rdd, stage.resultOfJob.get.func): AnyRef).array()
      }

      taskBinary = sc.broadcast(taskBinaryBytes)
    } catch {.....}

    val tasks: Seq[Task[_]] = try {
      stage match {//代码核心,不同的依赖不同的任务创建
        case stage: ShuffleMapStage =>
          partitionsToCompute.map { id =>
            val locs = getPreferredLocs(stage.rdd, id)
            val part = stage.rdd.partitions(id)
            new ShuffleMapTask(stage.id, taskBinary, part, locs)
          }

        case stage: ResultStage =>
          val job = stage.resultOfJob.get
          partitionsToCompute.map { id =>
            val p: Int = job.partitions(id)
            val part = stage.rdd.partitions(p)
            val locs = getPreferredLocs(stage.rdd, p)
            new ResultTask(stage.id, taskBinary, part, locs, id)
          }
      }
    } catch {...}

    这个时候才会发现任务才是任务的创建,也就是说只有在全部的stage提交之后,才会去提交task的时候创建与之相对应的task,我们可以看到,task是在每个RDD所对应的partition上面去创建的是吧。(●'◡'●)至于shuffleMapTask和resultTask的创建,在这里就不说啦,以后有空再看看。到这里仅仅是涉及task相关的创建,关键是task如何提交和运行的呢?哈哈代码还没完。

      这里就是就可以看到,就是taskScheduler把我们上面创建的tasks封装到一个taskSet中,然后再经过submitTask把任务提交给集群就可啦。具体的我们下次再看啦。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值