转载请标明出处:http://blog.csdn.net/bigbigdata/article/details/47310657

DAGScheduler通过调用submitStage来提交stage。实现例如以下:

  private def submitStage(stage: Stage) {
val jobId = activeJobForStage(stage)
if (jobId.isDefined) {
logDebug("submitStage(" + stage + ")")
if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
//< 获取该stage未提交的父stages,并按stage id从小到大排序
val missing = getMissingParentStages(stage).sortBy(_.id)
logDebug("missing: " + missing)
if (missing.isEmpty) {
logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
//< 若无未提交的父stage, 则提交该stage相应的tasks
submitMissingTasks(stage, jobId.get)
} else {
//< 若存在未提交的父stage, 依次提交全部父stage (若父stage也存在未提交的父stage, 则提交之, 依次类推); 并把该stage增加到等待stage队列中
for (parent <- missing) {
submitStage(parent)
}
waitingStages += stage
}
}
} else {
abortStage(stage, "No active job for stage " + stage.id)
}
}

submitStage先调用getMissingParentStages来获取參数stageX(这里为了区分,取名为stageX)是否有未提交的父stages,若有。则依次递归(按stage id从小到大排列。也就是stage是从后往前提交的)提交父stages,并将stageX增加到waitingStages: HashSet[Stage]中。对于要依次提交的父stage。也是如此。

getMissingParentStagesDAGScheduler划分stage中介绍的getParentStages有点像,但不同的是不再须要划分stage,并对每一个stage的状态做了推断,源代码及凝视例如以下:

//< 以參数stage为起点,向前遍历全部stage,推断stage是否为未提交,若使则增加missing中
private def getMissingParentStages(stage: Stage): List[Stage] = {
//< 未提交的stage
val missing = new HashSet[Stage]
//< 存储已经被訪问到得RDD
val visited = new HashSet[RDD[_]] val waitingForVisit = new Stack[RDD[_]]
def visit(rdd: RDD[_]) {
if (!visited(rdd)) {
visited += rdd
if (getCacheLocs(rdd).contains(Nil)) {
for (dep <- rdd.dependencies) {
dep match {
//< 若为宽依赖。生成新的stage
case shufDep: ShuffleDependency[_, _, _] =>
//< 这里调用getShuffleMapStage不像在getParentStages时须要划分stage。而是直接依据shufDep.shuffleId获取相应的ShuffleMapStage
val mapStage = getShuffleMapStage(shufDep, stage.jobId)
if (!mapStage.isAvailable) {
// 若stage得状态为available则为未提交stage
missing += mapStage
}
//< 若为窄依赖,那就属于同一个stage。并将依赖的RDD放入waitingForVisit中,以可以在以下的while中继续向上visit。直至遍历了整个DAG图
case narrowDep: NarrowDependency[_] =>
waitingForVisit.push(narrowDep.rdd)
}
}
}
}
}
waitingForVisit.push(stage.rdd)
while (waitingForVisit.nonEmpty) {
visit(waitingForVisit.pop())
}
missing.toList
}

上面提到,若stageX存在未提交的父stages。则先提交父stages;那么,假设stageX没有未提交的父stage呢(比方。包括从HDFS读取数据生成HadoopRDD的那个stage是没有父stage的)?

这时会调用submitMissingTasks(stage, jobId.get),參数就是stageX及其相应的jobId.get。这个函数便是我们时常在其它文章或书籍中看到的将stage与taskSet相应起来,然后DAGScheduler将taskSet提交给TaskScheduler去运行的实施者。

这个函数的实现比較长。以下分段说明。

Step1: 得到RDD中须要计算的partition

对于Shuffle类型的stage,须要推断stage中是否缓存了该结果;对于Result类型的Final Stage。则推断计算Job中该partition是否已经计算完毕。

这么做(没有直接提交全部tasks)的原因是,stage中某个task运行失败其它运行成功的时候就须要找出这个失败的task相应要计算的partition而不是要计算全部partition

  private def submitMissingTasks(stage: Stage, jobId: Int) {
stage.pendingTasks.clear() //< 首先得到RDD中须要计算的partition
//< 对于Shuffle类型的stage,须要推断stage中是否缓存了该结果;
//< 对于Result类型的Final Stage,则推断计算Job中该partition是否已经计算完毕
//< 这么做的原因是。stage中某个task运行失败其它运行成功地时候就须要找出这个失败的task相应要计算的partition而不是要计算全部partition
val partitionsToCompute: Seq[Int] = {
stage match {
case stage: ShuffleMapStage =>
(0 until stage.numPartitions).filter(id => stage.outputLocs(id).isEmpty)
case stage: ResultStage =>
val job = stage.resultOfJob.get
(0 until job.numPartitions).filter(id => !job.finished(id))
}
}

Step2: 序列化task的binary

Executor可以通过广播变量得到它。每一个task运行的时候首先会反序列化

var taskBinary: Broadcast[Array[Byte]] = null
try {
// For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).
// For ResultTask, serialize and broadcast (rdd, func).
val taskBinaryBytes: Array[Byte] = stage match {
case stage: ShuffleMapStage =>
//< 对于ShuffleMapTask,将rdd及其依赖关系序列化。在Executor运行task之前会反序列化
closureSerializer.serialize((stage.rdd, stage.shuffleDep): AnyRef).array()
//< 对于ResultTask,对rdd及要在每一个partition上运行的func
case stage: ResultStage =>
closureSerializer.serialize((stage.rdd, stage.resultOfJob.get.func): AnyRef).array()
} //< 将序列化好的信息广播给全部的executor
taskBinary = sc.broadcast(taskBinaryBytes)
} catch {
// In the case of a failure during serialization, abort the stage.
case e: NotSerializableException =>
abortStage(stage, "Task not serializable: " + e.toString)
runningStages -= stage // Abort execution
return
case NonFatal(e) =>
abortStage(stage, s"Task serialization failed: $e\n${e.getStackTraceString}")
runningStages -= stage
return
}

Step3: 为每一个须要计算的partiton生成一个task

ShuffleMapStage相应的task全是ShuffleMapTask; ResultStage相应的全是ResultTask。task继承Serializable,要确保task是可序列化的。

val tasks: Seq[Task[_]] = stage match {
case stage: ShuffleMapStage =>
partitionsToCompute.map { id =>
val locs = getPreferredLocs(stage.rdd, id)
//< RDD相应的partition
val part = stage.rdd.partitions(id)
new ShuffleMapTask(stage.id, taskBinary, part, locs)
} case stage: ResultStage =>
val job = stage.resultOfJob.get
//< id为输出分区索引,表示reducerID
partitionsToCompute.map { id =>
val p: Int = job.partitions(id)
val part = stage.rdd.partitions(p)
val locs = getPreferredLocs(stage.rdd, p)
new ResultTask(stage.id, taskBinary, part, locs, id)
}
}

Step4: 提交tasks

先用tasks来初始化一个TaskSet对象。再调用TaskScheduler.submitTasks提交

stage.pendingTasks ++= tasks
logDebug("New pending tasks: " + stage.pendingTasks)
//< 提交TaskSet至TaskScheduler
taskScheduler.submitTasks(
new TaskSet(tasks.toArray, stage.id, stage.newAttemptId(), stage.jobId, properties))
//< 记录stage提交task的时间
stage.latestInfo.submissionTime = Some(clock.getTimeMillis())
} else {

以上,介绍了提交stage和提交tasks的实现。本文若有纰漏,请批评指正。

[Spark源代码剖析] DAGScheduler提交stage的更多相关文章

  1. [Spark源代码剖析] DAGScheduler划分stage

    转载请标明出处:http://blog.csdn.net/bigbigdata/article/details/47293263 本文基于Spark 1.3.1 先上一些stage相关的知识点: DA ...

  2. 【Spark Core】TaskScheduler源代码与任务提交原理浅析2

    引言 上一节<TaskScheduler源代码与任务提交原理浅析1>介绍了TaskScheduler的创建过程,在这一节中,我将承接<Stage生成和Stage源代码浅析>中的 ...

  3. Spark源码剖析 - 任务提交与执行

    1. 任务概述 任务提交与执行过程: 1) build operator DAG:此阶段主要完成RDD的转换及DAG的构建: 2) split graph into stages of tasks:此 ...

  4. Spark源代码分析之六:Task调度(二)

    话说在<Spark源代码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这种方法针对接收到的ReviveOffe ...

  5. 【原】Spark中Job的提交源码解读

    版权声明:本文为原创文章,未经允许不得转载. Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码 ...

  6. [Apache Spark源代码阅读]天堂之门——SparkContext解析

    略微了解Spark源代码的人应该都知道SparkContext,作为整个Project的程序入口,其重要性不言而喻,很多大牛也在源代码分析的文章中对其做了非常多相关的深入分析和解读.这里,结合自己前段 ...

  7. Spark分析之DAGScheduler

    DAGScheduler概述:是一个面向Stage层面的调度器: 主要入参有: dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, ...

  8. 【原】 Spark中Task的提交源码解读

    版权声明:本文为原创文章,未经允许不得转载. 复习内容: Spark中Stage的提交 http://www.cnblogs.com/yourarebest/p/5356769.html Spark中 ...

  9. JDK1.7中的ThreadPoolExecutor源代码剖析

    JDK1. 7中的ThreadPoolExecutor 线程池,顾名思义一个线程的池子,池子里存放了非常多能够复用的线程,假设不用线程池相似的容器,每当我们须要创建新的线程时都须要去new Threa ...

随机推荐

  1. ADO.NET之断开数据连接的数据库操作

    在ADO.NET对数据库操作时有两种方式一种时与数据库实时连接,第二种时断开连接的操作. 断开连接的操作使用SqlDataAdapter来实现,我们要把数据库中的表数据加载到winform中的data ...

  2. SpringCloud学习笔记(7)----Spring Cloud Netflix之负载均衡-Ribbon的深入理解

    1. 注解@LoadBalanced 作用:识别应用名称,并进行负载均衡. 2. 入口类:LoadBalancerAutoConfiguration 说明:类头上的注解可以知道Ribbon 实现的负载 ...

  3. FFT&NTT学习笔记

    具体原理就不讲了qwq,毕竟证明我也不太懂 FFT(快速傅立叶变换)&NTT(快速数论变换) FFT //求多项式乘积 //要求多项式A和多项式B的积多项式C //具体操作就是 //DFT(A ...

  4. luogu P3414 SAC#1 - 组合数(组合数学)

    题意 求sigma(C(n,i))其中C是组合数(即C(n,i)表示n个物品无顺序选取i个的方案数),i取从0到n所有偶数. 由于答案可能很大,请输出答案对6662333的余数. (n<=101 ...

  5. Docker学习总结(11)——八个Docker的真实应用场景

    [编者的话]Flux 7介绍了常用的8个Docker的真实使用场景,分别是简化配置.代码流水线管理.提高开发效率.隔离应用.整合服务器.调试能力.多租户环境.快速部署.我们一直在谈Docker,Doc ...

  6. ASP.NET-ajax.acionlink使用

    Ajax 属性的ActionLink方法可以创建一个具有异步行为的锚标签. ActionLink方法的第一个参数指定了链接文本,第二个参数是要异步调用的操作的名称.类似于同名的HTML辅助方法,AJA ...

  7. 11g v$wait_chains 与 hanganalyze

    11g之后,通过v$wait_chains视图诊断数据库hang和Contention   11g之前,通常我们数据库hang住了之后,我们会对数据库做hang analyze来进行分析,在11g之后 ...

  8. Android ADB工具-截图和录制视频(五)

    Android ADB工具-截图和录制视频(五) 标签(空格分隔): Android ADB 7. 截图和录制视 命令 功能 adb shell screencap –p <path/file& ...

  9. cocos2d_x_01_环境搭建

    终于效果图: Cocos2d-x-3.3 Mac 安装 下载地址: 參考文档:  在线API列表:  Cocos2d-x-3.3 版本号 从配置安装到创建项目都是命令行 官网下载最新版本号Cocos2 ...

  10. Android Camera+SurfaceView实现自己定义拍照

    对Activity强制横屏,保证预览方向正确. 使用OrientationEventListener监听设备方向.推断竖拍时,旋转照片后再保存.保证竖拍时预览图片和保存后的图片方向一致. 执行效果: ...