|NO.Z.00091|——————————|BigDataEnd|——|Hadoop&Spark.V07|——|Spark.v07|Spark 原理 源码|作业执行原理&Stage划分&提交ResultStage|
一、Stage划分
### --- 提交ResultStage
~~~ submitStage 方法会通过入参 ResultStage 逐层获取父stage,
~~~ 再从最上游stage开始逐步调用TaskScheduler.submitTasks 方法提交task集合,
~~~ 最后才提交ResultStage的task集合。
~~~ 先调用getMissingParentStages来获取是否有未提交的父stages。
~~~ 若有,则依次递归提交父stages,并将missing加入到waitingStages中。
~~~ 对于要依次提交的父stage,也是如此;
~~~ 若missing存在未提交的父stages,则先提交父stages;
~~~ 这时会调用submitMissingTasks(stage, jobId.get),参数就是missing及其对应的jobId.get。
~~~ 这个函数便是将stage与taskSet对应起来,
~~~ 然后DAGScheduler将taskSet提交给TaskScheduler去执行的实施者。
二、源码提取说明
### --- 源码提取说明
~~~ # 源码提取说明:DAGScheduler.scala
~~~ # 1061行~1082行
private def submitStage(stage: Stage) {
// 获取当前Stage对应的Job的ID
val jobId = activeJobForStage(stage)
if (jobId.isDefined) { // Job ID是定义的
logDebug("submitStage(" + stage + ")")
if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) { //当前Stage未提交
// 获取当前Stage的所有未提交的父Stage
val missing = getMissingParentStages(stage).sortBy(_.id)
logDebug("missing: " + missing)
if (missing.isEmpty) { // 不存在未提交的父Stage
logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
// 提交当前Stage所有未提交的Task
submitMissingTasks(stage, jobId.get)
} else { // 存在未提交的父Stage
// 提交所有未提交的父Stage
for (parent <- missing) {
submitStage(parent)
}
// 并且将当前Stage加入waitingStages集合中,当前Stage必须等待所有父Stage执行完成
waitingStages += stage
}
}
} else { // Job ID未定义,放弃提交当前Stage
abortStage(stage, "No active job for stage " + stage.id, None)
}
}
~~~ # 源码提取说明:DAGScheduler.scala
~~~ # 550行~574行
// 获取Stage的所有未提交的父Stage
// 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 Stack[RDD[_]]
// 定义visit()方法
def visit(rdd: RDD[_]) {
// 判断是否已经处理过
if (!visited(rdd)) { // 未处理过
// 添加到已处理集合进行记录
visited += rdd
/**
* 获取RDD各个分区的TaskLocation序列,判断是否包含Nil。
* Stage的RDD的分区中存在没有对应TaskLocation序列的分区,
* 则说明当前Stage的某个上游ShuffleMapStage的某个分区任务未执行。
*/
val rddHasUncachedPartitions = getCacheLocs(rdd).contains(Nil)
if (rddHasUncachedPartitions) { // TaskLocation序列包含Nil
// 遍历该rdd的所有依赖
for (dep <- rdd.dependencies) {
dep match {
case shufDep: ShuffleDependency[_, _, _] => // 是ShuffleDependency
// 获取该ShuffleDependency的上游第一个提交的ShuffleMapStage
val mapStage = getOrCreateShuffleMapStage(shufDep, stage.firstJobId)
if (!mapStage.isAvailable) { // 该ShuffleMapStage不可用
// 将其添加到missing集合进行记录
missing += mapStage
}
case narrowDep: NarrowDependency[_] => // 是NarrowDependency
// 将该窄依赖的rdd压入waitingForVisit栈中
waitingForVisit.push(narrowDep.rdd)
}
}
}
}
}
Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
——W.S.Landor
分类:
bdv018-spark.v03
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通