spark源码《四》task提交

接着spark源码《三》Stage划分继续扯,当stage所有的父stage都已经运行,则调submitMissingTasks(stage)方法,

submitMissingTasks()

val pendingTasks = new HashMap[Stage, HashSet[Task[_]]]
val finalStage = newStage(finalRdd, None) 
val numOutputParts: Int = partitions.size//分区个数
val finished = new Array[Boolean](numOutputParts)//存储已经运行的分区,一个分区对应一个task
val outputParts = partitions.toArray//分区

def submitMissingTasks(stage: Stage) {
        //pendingTasks存储的是stage未运行的task集合
        val myPending = pendingTasks.getOrElseUpdate(stage, new HashSet)
        var tasks = ArrayBuffer[Task[_]]()//存储要提交的task
        if (stage == finalStage) {//判断是否是finalStage

          for (id <- 0 until numOutputParts if (!finished(id))) {//遍历找出未运行的分区下标
            val part = outputParts(id)//通过下标找到分区
            val locs = getPreferredLocs(finalRdd, part)//获取优先位置
            
            //创建一个ResultTask,加入到要提交的task数组中
            tasks += new ResultTask(runId, finalStage.id, finalRdd, func, part, locs, id)
          }
        } else {
          //如果不是finalStage
          for (p <- 0 until stage.numPartitions if stage.outputLocs(p) == Nil) {
            //遍历分区,如果某个分区没有输出位置
            val locs = getPreferredLocs(stage.rdd, p)//获取优先位置
            //创建一个ShuffleMapTask
            tasks += new ShuffleMapTask(runId, stage.id, stage.rdd, stage.shuffleDep.get, p, locs)
          }
        }
        myPending ++= tasks //将tasks加到未运行的tasks集合
        submitTasks(tasks, runId)//提交tasks
      }

可以看到,如果stage是finalStage,则创建一个ResultTask,直接返回一个结果,如果不是finalStage,创建一个ShuffleMapTask,将计算完的数据写入磁盘,等待后续task拉取,ResultTask与ShuffleMapTask区别,可看spark源码《二》Task

接下来我们去看submitTasks()方法,

我看的是早期代码,用的是mesos调度,关于mesos调度的细节可以下载源码了解。

private val activeJobs = new HashMap[Int, Job]//存储jobId,new SimpleJob()
private var activeJobsQueue = new ArrayBuffer[Job]
private val jobTasks = new HashMap[Int, HashSet[String]]//存储jobId,task集合

def submitTasks(tasks: Seq[Task[_]], runId: Int) {
    logInfo("Got a job with " + tasks.size + " tasks")
    waitForRegister()//等待mesos注册
    this.synchronized {
      val jobId = newJobId()//生成jobId
      val myJob = new SimpleJob(this, tasks, runId, jobId)//创建SimpleJob实例
      activeJobs(jobId) = myJob//加入Map中
      activeJobsQueue += myJob//加入变长数组
      logInfo("Adding job with ID " + jobId)
      jobTasks(jobId) = HashSet.empty[String]
    }
    driver.reviveOffers();//请求资源执行myjob
  }

创建了SimpleJob实例后,就请求执行了。

下面写个例子,对提交整个过程做个小结

    val data=sc.parallelize(List("tom","jerry","zlq","wnn","hehe","eason"),2)
    val mapd=data.map(x=>(x.length,1))
    val redd=mapd.reduceByKey(_+_,3)//3为新RDD的分区数
    val coud=redd.collect()

1.首先创建ParallelCollection(也继承RDD),分区为2,无依赖

2.然后执行map算子,生成MappedRDD,分区为2,依赖为 OneToOneDependency(ParallelCollection)

3.执行reduceByKey算子,生成ShuffleRDD,分区为3,依赖为ShuffleDependency(MappedRDD),

4.执行count算子,触发sc.runJob()-->DAGScheduler.runJob(ShuffleRDD)

创建stage:val finalStage = newStage(ShuffleRDD, None)

接下来:submitStage(finalStage),先调getMissingParentStages(finalStage)划分stage并获取父stage,

调用submitStage()提交父stage,submitMissingTasks(父stage)创建一个ShuffleMapTask,生成三个本地文件,等待ResultTask拉取。

猜你喜欢

转载自blog.csdn.net/zhaolq1024/article/details/82774792
今日推荐