Spark源码分析之四:Stage提交

        各位看官,上一篇《Spark源码分析之Stage划分》详细讲述了Spark中Stage的划分,下面,我们进入第三个阶段--Stage提交。

        Stage提交阶段的主要目的就一个,就是将每个Stage生成一组Task,即TaskSet,其处理流程如下图所示:


        与Stage划分阶段一样,我们还是从handleJobSubmitted()方法入手,在Stage划分阶段,包括最好的ResultStage和前面的若干ShuffleMapStage均已生成,那么顺理成章的下一步便是Stage的提交。在handleJobSubmitted()方法的最后两行代码,便是Stage提交的处理。代码如下:

// 提交最后一个stage
    submitStage(finalStage)

    // 提交其他正在等待的stage
    submitWaitingStages()
        从代码我们可以看出,Stage提交的逻辑顺序,是由后往前,即先提交最后一个finalStage,即ResultStage,然后再提交其parent stages,但是实际物理顺序是否如此呢?我们首先看下finalStage的提交,方法submitStage()代码如下:

/** Submits stage, but first recursively submits any missing parents. */
  // 提交stage,但是首先要递归的提交所有的missing父stage
  private def submitStage(stage: Stage) {
    
    // 根据stage获取jobId
    val jobId = activeJobForStage(stage)
    
    if (jobId.isDefined) {// 如果jobId已定义
      
      // 记录Debug日志信息:submitStage(stage)
      logDebug("submitStage(" + stage + ")")
      
      // 如果在waitingStages、runningStages或
      // failedStages任意一个中,不予处理
      
      // 既不在waitingStages中,也不在runningStages中,还不在failedStages中
      // 说明未处理过
      if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
        
        // 调用getMissingParentStages()方法,获取stage还没有提交的parent
        val missing = getMissingParentStages(stage).sortBy(_.id)
        
        logDebug("missing: " + missing)
        if (missing.isEmpty) {
          // 如果missing为空,说明是没有parent的stage或者其parent stages已提交,
          // 则调用submitMissingTasks()方法,提交tasks
          logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
          submitMissingTasks(stage, jobId.get)
        } else {
          // 否则,说明其parent还没有提交,递归,循环missing,提交每个stage
          for (parent <- missing) {
            submitStage(parent)
          }
          
          // 将该stage加入到waitingStages中
          waitingStages += stage
        }
      }
    } else {
      // 放弃该Stage
      abortStage(stage, "No active job for stage " + stage.id, None)
    }
  }
        代码逻辑比较简单。根据stage获取到jobId,如果jobId未定义,说明该stage不属于明确的Job,则调用abortStage()方法放弃该stage。如果jobId已定义的话,则需要判断该stage属于waitingStages、runningStages、failedStages中任意一个,则该stage忽略,不被处理。顾名思义,waitingStages为等待处理的stages,spark采取由后往前的顺序处理stage提交,即先处理child stage,然后再处理parent stage,所以位于waitingStages中的stage,由于其child stage尚未处理,所以必须等待,runningStages为正在运行的stages,正在运行意味着已经提交了,所以无需再提交,而最后的failedStages就是失败的stages,既然已经失败了,再提交也还是会失败,徒劳无益啊~

        此时,如果stage不位于上述三个数据结构中,则可以继续执行提交流程。接下来该怎么做呢?

        首先调用getMissingParentStages()方法,获取stage还没有提交的parent,即missing;如果missing为空,说明该stage要么没有parent stage,要么其parent stages都已被提交,此时该stage就可以被提交,用于提交的方法submitMissingTasks()我们稍后分析。

        如果missing不为空,则说明该stage还存在尚未被提交的parent stages,那么,我们就需要遍历missing,循环提交每个stage,并将该stage添加到waitingStages中,等待其parent stages都被提交后再被提交。

        我们先看下这个missing是如何获取的。进入getMissingParentStages()方法,代码如下:

private def getMissingParentStages(stage: Stage): List[Stage] = {
    
    // 存储尚未提交的parent stages,用于最后结果的返回
    val missing = new HashSet[Stage]
    
    // 已被处理的RDD集合
    val visited = new HashSet[RDD[_]]
    
    // We are manually maintaining a stack here to prevent StackOverflowE
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值