DAGScheduler提交job时,主要执行了:
val func2 = func.asInstanceOf[(TaskContext, Iterator[_]) => _] val waiter = new JobWaiter(this, jobId, partitions.size, resultHandler) eventProcessLoop.post(JobSubmitted( jobId, rdd, func2, partitions.toArray, callSite, waiter, SerializationUtils.clone(properties)))
eventProcessLoop的定义:
private[spark] val eventProcessLoop = new DAGSchedulerEventProcessLoop(this)
DAGSchedulerEventProcessLoop是class DAGScheduler的内部类,继承了EventLoop[DAGSchedulerEvent]。
**EventLoop**靠一个阻塞队列LinkedBlockingDeque保存事件队列,和一个eventThread线程来依次执行eventQueue的每一个事件。
线程函数名为onReceive(event)。
// EventLoop的post方法
def post(event: E): Unit = {
eventQueue.put(event)
}
DAGScheduler提交job时,向eventProcessLoop Post了JobSubmitted事件,
DAGSchedulerEventProcessLoop的 onReceive() 收到触发事件时,匹配到JobSubmitted事件,执行 handleJobSubmitted()
private def doOnReceive(event: DAGSchedulerEvent): Unit = event match {
case JobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties) =>
dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties)
case ...
}
在handleJobSubmitted()中,
- // 使用触发job的最后一个RDD来创建finalStage,new了一个 ResultStage
try{ finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite) } - // 用finalStage创建一个Job
val job = new ActiveJob(jobId, finalStage, callSite, listener, properties) - 把这个job保存到HashMap中,保存到activeJobs,添加引用到finalStage中
- 最后submitStage(finalStage)。这个方法会在递归后,反过来先提交第一个stage,
然后执行到submitMissingTasks()最后的submitWaitingChildStages()时提交其他stage。
submitStage()提交stage, 先递归地提交所有missing parents的父stage.
为什么把missing parents的stage叫父stage呢?
一个Stage可能有多个RDD,RDD已经根据Action之前的Transformation算子建立了依赖关系。
submitStage()这里,spark要根据RDD的依赖关系,从最后一个stage开始往前追溯,把所有stage连接起来。
所谓的getMissingParentStages()其实是消除Missing的过程,也就是从最后一个孩子起,认祖归宗,连接出所有Parents。
所以,从finalStage起,获取到的missing stage都必是父Stage!
/** 提交stage, 先递归地提交所有missing parents的父stage. */
private def submitStage(stage: Stage) {
// 查找需要这个stage的最早创建的ActiveJob
val jobId = activeJobForStage(stage)
if (jobId.isDefined) {
logDebug("submitStage(" + stage + ")")
if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
// 获取当前Stage尚未提交的所有父stages
val missing = getMissingParentStages(stage).sortBy(_.id)
logDebug("missing: " + missing)
// 一直递归调用,直到最初的stage没有父stage了,就首先提交这个领头stage,
// 此时,其余的stage全都被放到waitingStages队列了。
if (missing.isEmpty) {
logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
submitMissingTasks(stage, jobId.get)
} else {
for (parent <- missing) {
// 调用自己,递归地提交父stages
submitStage(parent)
}
// 把当前stage加入到waitingStages等待队列中
waitingStages += stage
}
}
} else {
abortStage(stage, "No active job for stage " + stage.id, None)
}
}
// 如果一个stage的最后一个rdd的所有依赖都是窄依赖,就不会创建新的stage;
// 只要这个stage的rdd依赖了一个宽依赖,就用那个宽依赖的rdd,创建一个新的stage(ShuffleMapStage)
// 作为当前Stage的父stage,并且将所有的父stages返回。
private def getMissingParentStages(stage: Stage): List[Stage] = {
val missing = new HashSet[Stage]
val visited = new HashSet[RDD[_]]
// We are manually maintaining a stack here to prevent StackOverflowError
// caused by recursively visiting
val waitingForVisit = new ArrayStack[RDD[_]]
def visit(rdd: RDD[_]) {
if (!visited(rdd)) {
visited += rdd
val rddHasUncachedPartitions = getCacheLocs(rdd).contains(Nil)
if (rddHasUncachedPartitions) {
// 遍历RDD的依赖
for (dep <- rdd.dependencies) {
dep match {
// 如果是宽依赖(ShuffleDependency),就对宽依赖的RDD创建一个ShuffleMapStage对象;
// 然后把新建的shuffle map的stage放入到 missing parent的集合中去。
// 默认宽依赖的最后一个stage不是ShuffleMapStage,但是finalStage之前的归入ShuffleMapStage,
// 当前宽依赖对应的RDD,就作为这个宽依赖的最后一个RDD;
case shufDep: ShuffleDependency[_, _, _] =>
val mapStage = getOrCreateShuffleMapStage(shufDep, stage.firstJobId)
if (!mapStage.isAvailable) {
missing += mapStage
}
// 如果是窄依赖,就把窄依赖的RDD入栈;
case narrowDep: NarrowDependency[_] =>
waitingForVisit.push(narrowDep.rdd)
}
}
}
}
}
// 首先往栈中推入最后一个stage的RDD
waitingForVisit.push(stage.rdd)
while (waitingForVisit.nonEmpty) {
// 对最后一个stage的RDD,调用内部函数visit()
visit(waitingForVisit.pop())
}
missing.toList
}
getMissingParentStages()方法,首先往栈中推入最后一个stage的RDD,
然后visit()栈中pop出的每一个RDD:
- 如果是窄依赖,就把窄依赖的RDD推入waitingForVisit栈,返回这里,继续visit!实现回溯。
- 如果是宽依赖,就getOrCreate一个ShuffleMapStage,把当前stage的第一个RDD作为shuffle stage的最后一个,
划分出新的Stage。
def createShuffleMapStage(shuffleDep: ShuffleDependency[_, _, _], jobId: Int): ShuffleMapStage = {
val rdd = shuffleDep.rdd
…
}