接着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拉取。