DAGScheduler生成各个stage的细节--getAncestorShuffleDependencies()展开去:为什么stage是套着的以及stage1,stage2怎么生成的

基本的划分流程请见https://www.jianshu.com/p/50c5c1032206,这里有一点异议。
上图:
这里写图片描述

在DAGSchedulerEventProcessLoop接收到JobSubmitted事件后,调用handleJobSubmitted方法,里面调用了newStage方法,这里第一次调用,传入newStage方法的是finalRDD

进入newStage方法,里面调用了getParentStages方法:

val parentStages: List[Stage] = getParentStages(rdd, jobId)
获取父stage列表,进入getParentStages方法:
        private def getParentStages(rdd: RDD[_], jobId: Int): List[Stage] = {
            val parents = new HashSet[Stage]
            val visited = new HashSet[RDD[_]]
            // We are manually maintaining a stack here to prevent StackOverflowError
            // caused by recursively visiting
            /*TODO 注意这个栈,里面保存RDD*/
            val waitingForVisit = new Stack[RDD[_]]

            /*TODO visit里面是栈弹出的finalRDD*/
            def visit(r: RDD[_]) {
                //finalRDD
                //如果visited中没有r,执行if条件中的代码
                if (!visited(r)) { //scala:这个r不在visited的HashSet中,返回false,即visited为空进入if
                    visited += r //将下面waitingForVisit弹出的这个r加入到visited中
                    // Kind of ugly: need to register RDDs with the cache here since
                    // we can't do it in its constructor because # of partitions is unknown
                    //TODO 获取这个r的依赖,因为父rdd可能是多个,所以依赖是多个--dependencies为复数
                    for (dep <- r.dependencies) {   //当前rdd的上一级依赖,可能是多个依赖,遍历这些依赖
                        dep match {

                            /*TODO 判断依赖是宽依赖ShuffleDependency*/
                            case shufDep: ShuffleDependency[_, _, _] =>
                                /*TODO getShuffleMapStage方法和stage的划分有关
                                * TODO getShuffleMapStage是递归的方法,
                                *   1,有宽依赖,就执行getShuffleMapStage方法
                                *   2,是窄依赖,就进行压栈
                                *   3,返回的是stage,放入val parents = new HashSet[Stage]中
                                * */

                                /*TODO  shufDep及其祖先的宽依赖的stage都放到shuffleToMapStage映射中
                                * */
                                parents += getShuffleMapStage(shufDep, jobId)

                            /*TODO 其他的是窄依赖*/
                            case _ =>
                                /*TODO 将这个依赖 dep即父rdd放入到waitingForVisit栈中,继续找窄依赖rdd的宽依赖*/
                                waitingForVisit.push(dep.rdd)
                        }
                    }
                }
            }

            waitingForVisit.push(rdd)

            /*TODO 当这个等待访问的栈不为空,就将其中的RDD弹出*/
            while (!waitingForVisit.isEmpty) {
                visit(waitingForVisit.pop())    //当获得finalRDD的上一级依赖对应的rdd被压入栈中再弹出,继续判断
            }
            /*Todo 返回当前rdd的所有的祖先的stage*/
            parents.toList
        }

进入getShuffleMapStage方法:

        private def getShuffleMapStage(
                                        shuffleDep: ShuffleDependency[_, _, _],
                                        jobId: Int): Stage = {
            shuffleToMapStage.get(shuffleDep.shuffleId) match {
                case Some(stage) => stage
                case None =>
                    // We are going to register ancestor shuffle dependencies
                    registerShuffleDependencies(shuffleDep, jobId)

                    // Then register current shuffleDep
                    val stage = newOrUsedStage(
                        shuffleDep.rdd, shuffleDep.rdd.partitions.size, shuffleDep, jobId,
                        shuffleDep.rdd.creationSite)

                    /*TODO 注意这个映射shuffleToMapStage,里面保存的是shuffleId->stage*/
                    shuffleToMapStage(shuffleDep.shuffleId) = stage

                    stage
            }
        }

进入registerShuffleDependencies方法:

        private def registerShuffleDependencies(shuffleDep: ShuffleDependency[_, _, _], jobId: Int) = {
            /*TODO 传进来的shuffleDep的rdd的所有宽依赖集合*/
            val parentsWithNoMapStage: mutable.Stack[ShuffleDependency[_, _, _]] = getAncestorShuffleDependencies(shuffleDep.rdd)
            while (!parentsWithNoMapStage.isEmpty) {

                /*根据这个循环,pop的第一个是最远处即最左端的宽依赖,依据此宽依赖和该宽依赖的rdd调用newOrUsedStage方法*/
                val currentShufDep = parentsWithNoMapStage.pop()
                val stage =
                    newOrUsedStage(
                        currentShufDep.rdd, currentShufDep.rdd.partitions.size, currentShufDep, jobId,
                        currentShufDep.rdd.creationSite)

                /*TODO 将所有宽依赖创建|获取的stage注册到shuffleToMapStage中*/
                shuffleToMapStage(currentShufDep.shuffleId) = stage
            }
        }

进入主角:getAncestorShuffleDependencies方法

    /*TODO 传进来的rdd是某个宽依赖的rdd*/
    private def getAncestorShuffleDependencies(rdd: RDD[_]): Stack[ShuffleDependency[_, _, _]] = {
        val parents = new Stack[ShuffleDependency[_, _, _]]
        val visited = new HashSet[RDD[_]]
        // We are manually maintaining a stack here to prevent StackOverflowError
        // caused by recursively visiting
        val waitingForVisit = new Stack[RDD[_]]

        def visit(r: RDD[_]) {
            if (!visited(r)) {
                visited += r
                for (dep <- r.dependencies) {
                    dep match {
                        case shufDep: ShuffleDependency[_, _, _] =>
                            if (!shuffleToMapStage.contains(shufDep.shuffleId)) {
                                /*TODO 如果shuffleToMapStage不包含该宽依赖的id,则该宽依赖放入parents中,
                                        因为是新进来的需要被注册的stage,所以第一次找基于当前传进来的rdd的所有祖先宽依赖
                                  TODO parents是栈,栈顶是最远处的即最左端的宽依赖
                                */
                                parents.push(shufDep)
                            }

                            /*TODO 即便遇到宽依赖也将宽依赖的rdd压栈,意味着有传进来的rdd获得所有该rdd祖先的全部宽依赖*/
                            waitingForVisit.push(shufDep.rdd)
                        case _ =>
                            /*TODO 遇到窄依赖,将所依赖的rdd压栈*/
                            waitingForVisit.push(dep.rdd)
                    }
                }
            }
        }

        waitingForVisit.push(rdd)
        while (!waitingForVisit.isEmpty) {
            visit(waitingForVisit.pop())
        }
        parents //返回所有的宽依赖
    }

getAncestorShuffleDependencies方法中:
1,注释标注了,是基于传递进来的rdd来查找这个rdd的所有祖先的宽依赖,不仅仅是父宽依赖,在循环调用visit方法过程中,parents栈顶存放的是与该rdd距离最远的宽依赖,比如上图中的stage1中rddA,也就是B的宽依赖
2,返回到调用getAncestorShuffleDependencies方法的registerShuffleDependencies中,返回当前rdd的所有祖先宽依赖后:

        /*TODO 传进来的shuffleDep的rdd的所有宽依赖集合*/
        val parentsWithNoMapStage: mutable.Stack[ShuffleDependency[_, _, _]] = getAncestorShuffleDependencies(shuffleDep.rdd)

        上面的代码在对应方法中往下看,到这里:
        val currentShufDep = parentsWithNoMapStage.pop()

parentsWithNoMapStage.pop()在一个循环中,不断的弹栈,弹出的即是从最左端的宽依赖,再往右弹:第二左宽依赖,第三左宽依赖。。。。这能理解吧。。。
3,紧接着会调用newOrUsedStage方法,该方法中有newStage方法,注意传递的参数,这里只考虑rdd的传递,如下:

        /*TODO 这个方法的返回值被称作finalStage*/
        private def newStage(
                                    rdd: RDD[_],    //这里传递进来的是finalRDD
                                    numTasks: Int,
                                    shuffleDep: Option[ShuffleDependency[_, _, _]],
                                    jobId: Int,
                                    callSite: CallSite)
        : Stage = {

            val parentStages: List[Stage] = getParentStages(rdd, jobId)  //传递finalRDD

            /*TODO nextStageId是AtomicInteger*/
            val id = nextStageId.getAndIncrement()

            //这里new了一个stage,注意传递的参数,rdd和parentStages,也就是这个new的stage会将rdd的父stage包起来
            //同时注意,这里的rdd是宽依赖对应的rdd,也就是上图中B的宽依赖A或者G的宽依赖F
            val stage = new Stage(id, rdd, numTasks, shuffleDep, parentStages, jobId, callSite)

            stageIdToStage(id) = stage
            updateJobIdStageIdMaps(jobId, stage)

            /*TODO 这个返回值被称作finalStage*/
            stage
        }
说明:
1,在整个递归循环找stage或者宽依赖过程中,最先能返回stage的是最左端的宽依赖
2,返回祖先所有宽依赖后,循环中parents弹出的第一个是最左端的宽依赖,依据此宽依赖对应的rdd进入这个newStage方法

3,进入newStage方法后,依然会执行getParentStages方法,但是已经没有父或者祖先的可追溯的东西了,也就是parentStages为空,然后基于原子整数成员变量nextStageId获得一个id val id = nextStageId.getAndIncrement(),最左的这个stage为0
4,接下来val stage = new Stage(id, rdd, numTasks, shuffleDep, parentStages, jobId, callSite),这里将id,rdd,parentStages都传进来创建一个stage对象,即第一个stage:stage0
5,创建完stage0后就可以开始返回stage了,到上一层newOrUsedStage方法中,这里经过一系列操作,仍然将stage0返回给上一层registerShuffleDependencies方法中,得到stage0后将其注册到shuffleToMapStage中
shuffleToMapStage(currentShufDep.shuffleId) = stage
6,这里:注册后还在之前提到的弹出祖先宽依赖的循环中。这里开始弹第二左宽依赖了!!!弹出后依然进入newOrUsedStage方法中的newStage方法,这里传进来的是第二左宽依赖对应的rdd,这里说一下newStage方法中的getParentStages方法,该方法依然会找父stage,不同的是:

a,stage0已经注册到了shuffleToMapStage中
b,getParentStages中找宽依赖还会调用getShuffleMapStage方法,
    但该方法中会依据当前rdd的宽依赖找shuffleToMapStage中的stage,
    第二宽依赖的父宽依赖对应的stage即stage0
c,所以,getParentStages返回的就是包含stage0的List[Stage]

7,接着看newStage执行完val parentStages: List[Stage] = getParentStages(rdd, jobId)后和stage0的方式一样,这里原子递增了一个id,即1,和当前第二左宽依赖对应的rdd和该rdd的父List[Stage]即包含stage0的parentStages,执行new stage对象,这里创建的就是stage1。对应上图中的就是stage3与stage1和stage2的关系
8,返回stage1后注册到shuffleToMapStage中
9,解释上图stage1和stage2谁先谁后,这个是基于parents这个宽窄依赖的栈中位置得到的,窄依赖多,那就靠栈顶一些,stage编号小一些;宽依赖多,就相对靠后,rdd的依赖是多个的,图中体现就是F->G以及A->B->G的关系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值