在上文《Spark技术内幕:Stage划分及提交源代码分析》中,我们分析了Stage的生成和提交。可是Stage的提交,仅仅是DAGScheduler完毕了对DAG的划分,生成了一个计算拓扑,即须要依照顺序计算的Stage,Stage中包括了能够以partition为单位并行计算的Task。我们并没有分析Stage中得Task是怎样生成而且终于提交到Executor中去的。

这就是本文的主题。

从org.apache.spark.scheduler.DAGScheduler#submitMissingTasks開始,分析Stage是怎样生成TaskSet的。

假设一个Stage的全部的parent stage都已经计算完毕或者存在于cache中。那么他会调用submitMissingTasks来提交该Stage所包括的Tasks。

org.apache.spark.scheduler.DAGScheduler#submitMissingTasks的计算流程例如以下:

  1. 首先得到RDD中须要计算的partition,对于Shuffle类型的stage。须要推断stage中是否缓存了该结果;对于Result类型的Final Stage。则推断计算Job中该partition是否已经计算完毕。
  2. 序列化task的binary。Executor能够通过广播变量得到它。每一个task执行的时候首先会反序列化。这样在不同的executor上执行的task是隔离的,不会相互影响。
  3. 为每一个须要计算的partition生成一个task:对于Shuffle类型依赖的Stage,生成ShuffleMapTask类型的task;对于Result类型的Stage,生成一个ResultTask类型的task
  4. 确保Task是能够被序列化的。由于不同的cluster有不同的taskScheduler,在这里推断能够简化逻辑。保证TaskSet的task都是能够序列化的
  5. 通过TaskScheduler提交TaskSet。

TaskSet就是能够做pipeline的一组全然同样的task,每一个task的处理逻辑全然同样。不同的是处理数据。每一个task负责处理一个partition。

pipeline。能够称为大数据处理的基石。仅仅有数据进行pipeline处理,才干将其放到集群中去执行。

对于一个task来说,它从数据源获得逻辑。然后依照拓扑顺序,顺序执行(实际上是调用rdd的compute)。

TaskSet是一个数据结构,存储了这一组task:
private[spark] class TaskSet(
val tasks: Array[Task[_]],
val stageId: Int,
val attempt: Int,
val priority: Int,
val properties: Properties) {
val id: String = stageId + "." + attempt override def toString: String = "TaskSet " + id
}
管理调度这个TaskSet的时org.apache.spark.scheduler.TaskSetManager。TaskSetManager会负责task的失败重试。跟踪每一个task的执行状态。处理locality-aware的调用。
具体的调用堆栈例如以下:
  1. org.apache.spark.scheduler.TaskSchedulerImpl#submitTasks
  2. org.apache.spark.scheduler.SchedulableBuilder#addTaskSetManager
  3. org.apache.spark.scheduler.cluster.CoarseGrainedSchedulerBackend#reviveOffers
  4. org.apache.spark.scheduler.cluster.CoarseGrainedSchedulerBackend.DriverActor#makeOffers
  5. org.apache.spark.scheduler.TaskSchedulerImpl#resourceOffers
  6. org.apache.spark.scheduler.cluster.CoarseGrainedSchedulerBackend.DriverActor#launchTasks
  7. org.apache.spark.executor.CoarseGrainedExecutorBackend.receiveWithLogging#launchTask
  8. org.apache.spark.executor.Executor#launchTask
首先看一下org.apache.spark.executor.Executor#launchTask:
  def launchTask(
context: ExecutorBackend, taskId: Long, taskName: String, serializedTask: ByteBuffer) {
val tr = new TaskRunner(context, taskId, taskName, serializedTask)
runningTasks.put(taskId, tr)
threadPool.execute(tr) // 開始在executor中执行
}
TaskRunner会从序列化的task中反序列化得到task。这个须要看 org.apache.spark.executor.Executor.TaskRunner#run 的实现:task.run(taskId.toInt)。而task.run的实现是:
 final def run(attemptId: Long): T = {
context = new TaskContext(stageId, partitionId, attemptId, runningLocally = false)
context.taskMetrics.hostname = Utils.localHostName()
taskThread = Thread.currentThread()
if (_killed) {
kill(interruptThread = false)
}
runTask(context)
}

对于原来提到的两种Task,即

  1. org.apache.spark.scheduler.ShuffleMapTask
  2. org.apache.spark.scheduler.ResultTask
分别实现了不同的runTask:
org.apache.spark.scheduler.ResultTask#runTask即顺序调用rdd的compute,通过rdd的拓扑顺序依次对partition进行计算:
  override def runTask(context: TaskContext): U = {
// Deserialize the RDD and the func using the broadcast variables.
val ser = SparkEnv.get.closureSerializer.newInstance()
val (rdd, func) = ser.deserialize[(RDD[T], (TaskContext, Iterator[T]) => U)](
ByteBuffer.wrap(taskBinary.value), Thread.currentThread.getContextClassLoader) metrics = Some(context.taskMetrics)
try {
func(context, rdd.iterator(partition, context))
} finally {
context.markTaskCompleted()
}
}
而org.apache.spark.scheduler.ShuffleMapTask#runTask则是写shuffle的结果。
  override def runTask(context: TaskContext): MapStatus = {
// Deserialize the RDD using the broadcast variable.
val ser = SparkEnv.get.closureSerializer.newInstance()
val (rdd, dep) = ser.deserialize[(RDD[_], ShuffleDependency[_, _, _])](
ByteBuffer.wrap(taskBinary.value), Thread.currentThread.getContextClassLoader)
//此处的taskBinary即为在org.apache.spark.scheduler.DAGScheduler#submitMissingTasks序列化的task的广播变量取得的 metrics = Some(context.taskMetrics)
var writer: ShuffleWriter[Any, Any] = null
try {
val manager = SparkEnv.get.shuffleManager
writer = manager.getWriter[Any, Any](dep.shuffleHandle, partitionId, context)
writer.write(rdd.iterator(partition, context).asInstanceOf[Iterator[_ <: Product2[Any, Any]]]) // 将rdd计算的结果写入memory或者disk
return writer.stop(success = true).get
} catch {
case e: Exception =>
if (writer != null) {
writer.stop(success = false)
}
throw e
} finally {
context.markTaskCompleted()
}
}
这两个task都不要依照拓扑顺序调用rdd的compute来完毕对partition的计算。不同的是ShuffleMapTask须要shuffle write。以供child stage读取shuffle的结果。

对于这两个task都用到的taskBinary,即为在org.apache.spark.scheduler.DAGScheduler#submitMissingTasks序列化的task的广播变量取得的。

通过上述几篇博文,实际上我们已经粗略的分析了从用户定义SparkContext開始。集群是假设为每一个Application分配Executor的,回想一下这个序列图:
还有就是用户触发某个action,集群是怎样生成DAG,假设将DAG划分为能够成Stage,已经Stage是怎样将这些能够pipeline执行的task提交到Executor去执行的。当然了,具体细节还是很值得推敲的。

以后的每一个周末。都会奉上某个细节的实现。

歇息了。明天又会開始忙碌的一周。

Spark技术内幕: Task向Executor提交的源代码解析的更多相关文章

  1. Spark技术内幕: Task向Executor提交的源码解析

    在上文<Spark技术内幕:Stage划分及提交源码分析>中,我们分析了Stage的生成和提交.但是Stage的提交,只是DAGScheduler完成了对DAG的划分,生成了一个计算拓扑, ...

  2. Spark技术内幕:Worker源码与架构解析

    首先通过一张Spark的架构图来了解Worker在Spark中的作用和地位: Worker所起的作用有以下几个: 1. 接受Master的指令,启动或者杀掉Executor 2. 接受Master的指 ...

  3. Spark技术内幕:Stage划分及提交源码分析

    http://blog.csdn.net/anzhsoft/article/details/39859463 当触发一个RDD的action后,以count为例,调用关系如下: org.apache. ...

  4. Spark技术内幕:Master的故障恢复

    Spark技术内幕:Master基于ZooKeeper的High Availability(HA)源码实现  详细阐述了使用ZK实现的Master的HA,那么Master是如何快速故障恢复的呢? 处于 ...

  5. Spark技术内幕:Stage划分及提交源代码分析

    当触发一个RDD的action后.以count为例,调用关系例如以下: org.apache.spark.rdd.RDD#count org.apache.spark.SparkContext#run ...

  6. Spark技术内幕:Shuffle Map Task运算结果的处理

    Shuffle Map Task运算结果的处理 这个结果的处理,分为两部分,一个是在Executor端是如何直接处理Task的结果的:还有就是Driver端,如果在接到Task运行结束的消息时,如何对 ...

  7. 我的第一本著作:Spark技术内幕上市!

    现在各大网站销售中! 京东:http://item.jd.com/11770787.html 当当:http://product.dangdang.com/23776595.html 亚马逊:http ...

  8. Spark技术内幕:Client,Master和Worker 通信源代码解析

    Spark的Cluster Manager能够有几种部署模式: Standlone Mesos YARN EC2 Local 在向集群提交计算任务后,系统的运算模型就是Driver Program定义 ...

  9. Spark技术内幕:Executor分配详解

    当用户应用new SparkContext后,集群就会为在Worker上分配executor,那么这个过程是什么呢?本文以Standalone的Cluster为例,详细的阐述这个过程.序列图如下: 1 ...

随机推荐

  1. HashSet源码分析 jdk1.6

    Set的特点:Set元素无顺序,且元素不可以重复. 1.定义 public class HashSet<E> extends AbstractSet<E> implements ...

  2. iOS-----openGL--openGL ES iOS 入门篇3---> 纹理贴图(texture)

    在这篇文章中,我们将学习如何在openGL中使用纹理贴图. penGL中纹理可以分为1D,2D和3D纹理,我们在绑定纹理对象的时候需要指定纹理的种类.由于本文将以一张图片为例,因此我们为我们的纹理对象 ...

  3. 【Luogu】P4357K远点对(寄蒜几盒)

    题目链接 考虑旋转卡壳求出一个最远点对之后删掉其中一个点,把该点到其余所有点的距离存进堆里…… 最后堆输出答案. 我的代码只有在开O2的情况下才不会re.为啥???? #include<cstd ...

  4. 【HNOI2011/bzoj2337】XOR和路径

    第二道高斯消元练习题 题意 一张无向图,从点 $1$ 出发每次随机选一条出边走,走到 $n$ 停止,求经过的所有边权异或和的期望. $n\le 100$ 题解 注意一点,异或和的期望 $≠$ 期望的异 ...

  5. java面试题之volatile的工作原理

    volatile的特性: volatile可见性:对一个volatile的读,总可以看到对这个变量最终的写: volatile原子性:volatile对单个读/写具有原子性(32位Long.Doubl ...

  6. hdu 3501 容斥原理或欧拉函数

    Calculation 2 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  7. 【BZOJ3524】Couriers(主席树)

    题意:给一个长度为n的序列a.1≤a[i]≤n.m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2.如果存在,输出这个数,否则输出0. n,m≤5000 ...

  8. Java基础加强-(注解,动态代理,类加载器,servlet3.0新特性)

    1.   Annotation注解 1.1.  Annotation概述 Annotation是JDK 5.0以后提供对元数据的支持,可以在编译.加载和运行时被读取,并执行相应的处理.所谓Annota ...

  9. 标准C程序设计七---104

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  10. PHP文件函数

    PHP文件函数 函数 描述 PHPbasename() 返回路径中的文件名部分. 3chgrp() 改变文件组. 3chmod() 改变文件模式. 3chown() 改变文件所有者. 3clearst ...