前两篇文章写了Shuffle Read的一些实现细节。但是要想彻底理清楚这里边的实现逻辑,还是需要更多篇幅的;本篇开始,将按照Job的执行顺序,来讲解Shuffle。即,结果数据(ShuffleMapTask的结果和ResultTask的结果)是如何产生的;结果是如何处理的;结果是如何读取的。

在Worker上接收Task执行命令的是org.apache.spark.executor.CoarseGrainedExecutorBackend。它在接收到LaunchTask的命令后,通过在Driver创建SparkContext时已经创建的org.apache.spark.executor.Executor的实例的launchTask,启动Task:

  deflaunchTask(
context: ExecutorBackend, taskId: Long, taskName: String,serializedTask: ByteBuffer) {
val tr = new TaskRunner(context, taskId, taskName, serializedTask)
runningTasks.put(taskId, tr)
threadPool.execute(tr) // 开始在executor中运行
}

最终Task的执行是在org.apache.spark.executor.Executor.TaskRunner#run。org.apache.spark.executor.ExecutorBackend是Executor与Driver通信的接口,它实际上是一个trait:

private[spark] trait ExecutorBackend {
defstatusUpdate(taskId: Long, state: TaskState, data: ByteBuffer)
}

TaskRunner会将Task执行的状态汇报给Driver(org.apache.spark.scheduler.cluster.CoarseGrainedSchedulerBackend.DriverActor)。 而Driver会转给org.apache.spark.scheduler.TaskSchedulerImpl#statusUpdate。

在Executor运行Task时,得到计算结果会存入org.apache.spark.scheduler.DirectTaskResult。在将结果回传到Driver时,会根据结果的大小有不同的策略:对于“较大”的结果,将其以taskid为key存入org.apache.spark.storage.BlockManager;如果结果不大,那么直接回传给Driver。那么如何判定这个阈值呢?

这里的回传是直接通过akka的消息传递机制。因此这个大小首先不能超过这个机制设置的消息的最大值。这个最大值是通过spark.akka.frameSize设置的,单位是Bytes,默认值是10MB。除此之外,还有200KB的预留空间。因此这个阈值就是conf.getInt("spark.akka.frameSize", 10) * 1024 *1024 – 200KB。

       // directSend = sending directly back to the driver
val (serializedResult, directSend) = {
if (resultSize >=akkaFrameSize - AkkaUtils.reservedSizeBytes) { //如果结果太大,那么存入BlockManager
val blockId = TaskResultBlockId(taskId)
env.blockManager.putBytes(
blockId, serializedDirectResult,StorageLevel.MEMORY_AND_DISK_SER)
(ser.serialize(new IndirectTaskResult[Any](blockId)), false)
} else { // 如果大小合适,则直接发送结果给Driver
(serializedDirectResult, true)
}
}
execBackend.statusUpdate(taskId, TaskState.FINISHED, serializedResult)

TaskRunner将Task的执行状态汇报给Driver后,Driver会转给org.apache.spark.scheduler.TaskSchedulerImpl#statusUpdate。而在这里不同的状态有不同的处理:

1.    如果类型是TaskState.FINISHED,那么调用org.apache.spark.scheduler.TaskResultGetter#enqueueSuccessfulTask进行处理。

2.    如果类型是TaskState.FAILED或者TaskState.KILLED或者TaskState.LOST,调用org.apache.spark.scheduler.TaskResultGetter#enqueueFailedTask进行处理。对于TaskState.LOST,还需要将其所在的Executor标记为failed, 并且根据更新后的Executor重新调度。

enqueueSuccessfulTask的逻辑也比较简单,就是如果是IndirectTaskResult,那么需要通过blockid来获取结果:sparkEnv.blockManager.getRemoteBytes(blockId);如果是DirectTaskResult,那么结果就无需远程获取了。然后调用

1.    org.apache.spark.scheduler.TaskSchedulerImpl#handleSuccessfulTask

2.    org.apache.spark.scheduler.TaskSetManager#handleSuccessfulTask

3.    org.apache.spark.scheduler.DAGScheduler#taskEnded

4.    org.apache.spark.scheduler.DAGScheduler#eventProcessActor

5.    org.apache.spark.scheduler.DAGScheduler#handleTaskCompletion

进行处理。核心逻辑都在第5个调用栈。如果task是ResultTask,处理逻辑比较简单,停止job,更新一些状态,发送一些event即可。

    if (!job.finished(rt.outputId)){
job.finished(rt.outputId) =true
job.numFinished += 1
// If the whole job hasfinished, remove it
if (job.numFinished ==job.numPartitions) {
markStageAsFinished(stage)
cleanupStateForJobAndIndependentStages(job)
listenerBus.post(SparkListenerJobEnd(job.jobId,JobSucceeded))
} // taskSucceeded runs someuser code that might throw an exception.
// Make sure we areresilient against that.
try {
job.listener.taskSucceeded(rt.outputId, event.result)
} catch {
case e: Exception =>
// TODO: Perhaps we wantto mark the stage as failed?
job.listener.jobFailed(new SparkDriverExecutionException(e))
}
}

如果task是ShuffleMapTask,那么它需要将结果通过某种机制告诉下游的Stage,以便于其可以作为下游Stage的输入。这个机制是怎么实现的?

实际上,对于ShuffleMapTask来说,其结果实际上是org.apache.spark.scheduler.MapStatus;其序列化后存入了DirectTaskResult或者IndirectTaskResult中。而DAGScheduler#handleTaskCompletion通过下面的方式来获取这个结果:

val status =event.result.asInstanceOf[MapStatus]

通过将这个status注册到org.apache.spark.MapOutputTrackerMaster,就实现了

    mapOutputTracker.registerMapOutputs(
stage.shuffleDep.get.shuffleId,
stage.outputLocs.map(list=> if (list.isEmpty) null else list.head).toArray,
changeEpoch = true)

Spark技术内幕: Shuffle详解(三)的更多相关文章

  1. Spark技术内幕: Shuffle详解(一)

    通过上面一系列文章,我们知道在集群启动时,在Standalone模式下,Worker会向Master注册,使得Master可以感知进而管理整个集群:Master通过借助ZK,可以简单的实现HA:而应用 ...

  2. Spark技术内幕: Shuffle详解(二)

    本文主要关注ShuffledRDD的Shuffle Read是如何从其他的node上读取数据的. 上文讲到了获取如何获取的策略都在org.apache.spark.storage.BlockFetch ...

  3. [Spark内核] 第36课:TaskScheduler内幕天机解密:Spark shell案例运行日志详解、TaskScheduler和SchedulerBackend、FIFO与FAIR、Task运行时本地性算法详解等

    本課主題 通过 Spark-shell 窥探程序运行时的状况 TaskScheduler 与 SchedulerBackend 之间的关系 FIFO 与 FAIR 两种调度模式彻底解密 Task 数据 ...

  4. 前端技术之_CSS详解第三天

    前端技术之_CSS详解第三天 二.权重问题深入 2.1 同一个标签,携带了多个类名,有冲突: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...

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

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

  6. 前端技术之_CSS详解第一天

    前端技术之_CSS详解第一天 一html部分 略.... 二.列表 列表有3种 2.1 无序列表 无序列表,用来表示一个列表的语义,并且每个项目和每个项目之间,是不分先后的. ul就是英语unorde ...

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

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

  8. 前端技术之_CSS详解第四天

    前端技术之_CSS详解第四天 一.第三天的小总结 盒模型box model,什么是盒子? 所有的标签都是盒子.无论是div.span.a都是盒子.图片.表单元素一律看做文本. 盒模型有哪些组成: wi ...

  9. 前端技术之_CSS详解第五天

    前端技术之_CSS详解第五天 一.行高和字号 1.1 行高 CSS中,所有的行,都有行高.盒模型的padding,绝对不是直接作用在文字上的,而是作用在“行”上的. <!DOCTYPE html ...

随机推荐

  1. 《C++ Primer》学习笔记:向vector对象添加元素蕴含的编程假定

    练习<C++ Primer>中的3.14节时,当敲入: #include <iostream> #include <string> using namespace ...

  2. codevs 搜索题汇总(黄金级)

    2801 LOL-盖伦的蹲草计划  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold   题目描述 Description 众所周知,LOL这款伟大的游戏,有个叫盖 ...

  3. 想进大厂,想收获高薪offer,资深猎头告诉你怎么做......

    其实吧,面试官面试的时候主要就看三个方面:现在能力如何,未来潜力如何,人品如何. 第一个因素是最重要的,因为后面两个因素有太多的人为判断因素,无法量化.所谓的面试准备,"现在能力如何&quo ...

  4. 【NOIP 2017】列队

    Description Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵的行数为 n ...

  5. hdu4549(费马小定理 + 快速幂)

    M斐波那契数列F[n]是一种整数数列,它的定义如下: F[0] = a F[1] = b F[n] = F[n-1] * F[n-2] ( n > 1 ) 现在给出a, b, n,你能求出F[n ...

  6. [Codeforces]849E Goodbye Souvenir

    又是一道比较新的模板题吧,即使是在Codeforces上小C还是贴了出来. Description 给定一个长度为n的序列a1~an,每个元素代表一种颜色.m次操作,每次操作为两种中的一种: 1 p ...

  7. bzoj1044[HAOI2008]木棍分割 单调队列优化dp

    1044: [HAOI2008]木棍分割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4314  Solved: 1664[Submit][Stat ...

  8. VM11 CentOS6.7 i386 安装 oracle 11g r2

    CentOS 6.7 i386:最小桌面版本--中文1.网络配置 ifup eht0 vim /etc/sysconfig/network-scripts/ifcfg-eth0 修改:NBOOT=ye ...

  9. SpringBoot学习之SpringBoot执行器

    在以往的分布式开发当中,各个服务节点的监控必不可少.监控包含有很多方面,比如说:内存占用情况,节点是否健康等.在spring-boot会给我们提供相关资源监控叫做spring-boot-actuato ...

  10. AD域中添加了一个策略导致的问题

    AD域中添加了一个策略,导致浏览器报了一个错,点击下拉菜单选不了.查了资料说是添加信任站点就好,结果信任站点是置灰的,服务器有这个问题,本地是好的. 解决办法:本地服务器有个ie增强设置,关掉就好.