通过上一节内容,DriverEndpoint最终生成多个可执行的TaskDescription对象,并向各个ExecutorEndpoint发送LaunchTask指令,本节内容将关注ExecutorEndpoint如何处理LaunchTask指令,处理完成后如何回馈给DriverEndpoint,以及整个job最终如何多次调度直至结束。
 
一、Task的执行流程
     承接上一节内容,Executor接受LaunchTask指令后,开启一个新线程TaskRunner解析RDD,并调用RDD的compute方法,归并函数得到最终任务执行结果
     
  • ExecutorEndpoint接受到LaunchTask指令后,解码出TaskDescription,调用Executor的launchTask方法
  • Executor创建一个TaskRunner线程,并启动线程,同时将改线程添加到Executor的成员对象中,代码如下:
private val runningTasks = new ConcurrentHashMap[Long, TaskRunner]
runningTasks.put(taskDescription.taskId, taskRunner)
  • TaskRunner
    • 首先向DriverEndpoint发送任务最新状态为RUNNING
    • 从TaskDescription解析出Task,并调用Task的run方法
  • Task
    • 创建TaskContext以及CallerContext(与HDFS交互的上下文对象)
    • 执行Task的runTask方法
      • 如果Task实例为ShuffleMapTask:解析出RDD以及ShuffleDependency信息,调用RDD的compute()方法将结果写Writer中(Writer这里不介绍,可以作为黑盒理解,比如写入一个文件中),返回MapStatus对象
      • 如果Task实例为ResultTask:解析出RDD以及合并函数信息,调用函数将调用后的结果返回
  • TaskRunner将Task执行的结果序列化,再次向DriverEndpoint发送任务最新状态为FINISHED
 
二、Task的回馈流程
     TaskRunner执行结束后,都将执行状态发送至DriverEndpoint,DriverEndpoint最终反馈指令CompletionEvent至DAGSchedulerEventProcessLoop中
     
  • DriverEndpoint接受到StatusUpdate消息后,调用TaskScheduler的statusUpdate(taskId, state, result)方法
  • TaskScheduler如果任务结果是完成,那么清除该任务处理中的状态,并调动TaskResultGetter相关方法,关键代码如下:
val taskSet = taskIdToTaskSetManager.get(tid)

taskIdToTaskSetManager.remove(tid)
taskIdToExecutorId.remove(tid).foreach { executorId =>
executorIdToRunningTaskIds.get(executorId).foreach { _.remove(tid) }
}
taskSet.removeRunningTask(tid) if (state == TaskState.FINISHED) {
taskResultGetter.enqueueSuccessfulTask(taskSet, tid, serializedData)
} else if (Set(TaskState.FAILED, TaskState.KILLED, TaskState.LOST).contains(state)) {
taskResultGetter.enqueueFailedTask(taskSet, tid, state, serializedData)
}
  • TaskResultGetter启动线程启动线程【task-result-getter】进行相关处理
    • 通过解析或者远程获取得到Task的TaskResult对象
    • 调用TaskSet的handleSuccessfulTask方法,TaskSet的handleSuccessfulTask方法直接调用TaskSetManager的handleSuccessfulTask方法
  • TaskSetManager
    • 更新内部TaskInfo对象状态,并将该Task从运行中Task的集合删除,代码如下:
val info = taskInfos(tid)
info.markFinished(TaskState.FINISHED, clock.getTimeMillis())
removeRunningTask(tid)
    • 调用DAGScheduler的taskEnded方法,关键代码如下:
sched.dagScheduler.taskEnded(tasks(index), Success, result.value(), result.accumUpdates, info)
  • DAGScheduler向DAGSchedulerEventProcessLoop存入CompletionEvent指令,CompletionEvent对象定义如下
private[scheduler] case class CompletionEvent(
task: Task[_],
reason: TaskEndReason,
result: Any,
accumUpdates: Seq[AccumulatorV2[_, _]],
taskInfo: TaskInfo)
extends DAGSchedulerEvent
 
三、Task的迭代流程
     DAGSchedulerEventProcessLoop中针对于CompletionEvent指令,调用DAGScheduler进行处理,DAGScheduler更新Stage与该Task的关系状态,如果Stage下Task都返回,则做下一层Stage的任务拆解与运算工作,直至Job被执行完毕
  
  • DAGSchedulerEventProcessLoop接收到CompletionEvent指令后,调用DAGScheduler的handleTaskCompletion方法
  • DAGScheduler根据Task的类型分别处理
  • 如果Task为ShuffleMapTask
    • 待回馈的Partitions减取当前partitionId
    • 如果所有task都返回,则markStageAsFinished(shuffleStage),同时向MapOutputTrackerMaster注册MapOutputs信息,且markMapStageJobAsFinished
    • 调用submitWaitingChildStages(shuffleStage)进行下层Stages的处理,从而迭代处理最终处理到ResultTask,job结束,关键代码如下:
private def submitWaitingChildStages(parent: Stage) {
...
val childStages = waitingStages.filter(_.parents.contains(parent)).toArray
waitingStages --= childStages
for (stage <- childStages.sortBy(_.firstJobId)) {
submitStage(stage)
}
}
  • 如果Task为ResultTask
    • 改job的partitions都已返回,则markStageAsFinished(resultStage),并cleanupStateForJobAndIndependentStages(job),关键代码如下
for (stage <- stageIdToStage.get(stageId)) {
if (runningStages.contains(stage)) {
logDebug("Removing running stage %d".format(stageId))
runningStages -= stage
}
for ((k, v) <- shuffleIdToMapStage.find(_._2 == stage)) {
shuffleIdToMapStage.remove(k)
}
if (waitingStages.contains(stage)) {
logDebug("Removing stage %d from waiting set.".format(stageId))
waitingStages -= stage
}
if (failedStages.contains(stage)) {
logDebug("Removing stage %d from failed set.".format(stageId))
failedStages -= stage
}
}
// data structures based on StageId
stageIdToStage -= stageId
jobIdToStageIds -= job.jobId
jobIdToActiveJob -= job.jobId
activeJobs -= job
     至此,用户编写的代码最终调用Spark分布式计算完毕。

【Spark2.0源码学习】-10.Task执行与回馈的更多相关文章

  1. 【Spark2.0源码学习】-1.概述

          Spark作为当前主流的分布式计算框架,其高效性.通用性.易用性使其得到广泛的关注,本系列博客不会介绍其原理.安装与使用相关知识,将会从源码角度进行深度分析,理解其背后的设计精髓,以便后续 ...

  2. spark2.0源码学习

    [Spark2.0源码学习]-1.概述 [Spark2.0源码学习]-2.一切从脚本说起 [Spark2.0源码学习]-3.Endpoint模型介绍 [Spark2.0源码学习]-4.Master启动 ...

  3. 【Spark2.0源码学习】-2.一切从脚本说起

    从脚本说起      在看源码之前,我们一般会看相关脚本了解其初始化信息以及Bootstrap类,Spark也不例外,而Spark我们启动三端使用的脚本如下: %SPARK_HOME%/sbin/st ...

  4. 【Spark2.0源码学习】-3.Endpoint模型介绍

         Spark作为分布式计算框架,多个节点的设计与相互通信模式是其重要的组成部分.   一.组件概览      对源码分析,对于设计思路理解如下:            RpcEndpoint: ...

  5. 【Spark2.0源码学习】-9.Job提交与Task的拆分

          在前面的章节Client的加载中,Spark的DriverRunner已开始执行用户任务类(比如:org.apache.spark.examples.SparkPi),下面我们开始针对于用 ...

  6. 【Spark2.0源码学习】-6.Client启动

    Client作为Endpoint的具体实例,下面我们介绍一下Client启动以及OnStart指令后的额外工作 一.脚本概览      下面是一个举例: /opt/jdk1..0_79/bin/jav ...

  7. 【Spark2.0源码学习】-4.Master启动

         Master作为Endpoint的具体实例,下面我们介绍一下Master启动以及OnStart指令后的相关工作   一.脚本概览      下面是一个举例: /opt/jdk1..0_79/ ...

  8. 【Spark2.0源码学习】-5.Worker启动

         Worker作为Endpoint的具体实例,下面我们介绍一下Worker启动以及OnStart指令后的额外工作   一.脚本概览      下面是一个举例: /opt/jdk1..0_79/ ...

  9. 【Spark2.0源码学习】-7.Driver与DriverRunner

         承接上一节内容,Client向Master发起RequestSubmitDriver请求,Master将DriverInfo添加待调度列表中(waitingDrivers),下面针对于Dri ...

随机推荐

  1. PHP的laravel框架后台实现数据导出excel的功能

    要想在PHP后台实现excel导入导出功能,一种简单有效的方法就是使用phpexcel插件. 要使用phpexcel插件,首先需要下载composer,这个工具是专门用来管理项目中库之间的依赖关系的. ...

  2. POJ 3261 出现至少K次的可重叠最长子串

    题意就是给一列数字,求最长的一个子串,并且满足子串在原数串中出现至少K次,子串可以重叠. 解法是将问题转为判定性问题,二分子串的长度,判定是否满足重复至少K次.判定方法是经典的根据子串长度将Heigh ...

  3. sass学习入门篇(三)

    这章我们讲“嵌套”,嵌套包括两种:一,选择器嵌套.二是属性的嵌套.一般用选择器嵌套居多 一,选择器嵌套:指的是在一个选择器中嵌套另一个选择器来实现继承.使用&表示父元素选择器 li{ floa ...

  4. (原创)看我用各种姿势在手机和PC查看到连接到的wifi密码

    今天一个女神来我家做客,她问我WiFi密码,然而我却奇迹般的忘记了(特么的当时心里一万个草泥马踏过去),让我在她面前尴尬求子的,所以为了防止你们也出现这种情况,我特地把各种方法整理了一下,那么感兴趣的 ...

  5. [ext4]磁盘布局 - group分析

    ext4文件系统的磁盘布局是先把磁盘分成一个个相同大小的block块(每个block块的大小默认是4K),然后把这些block块合成一个个group. group大小最大为256M(默认为256M), ...

  6. JQuery实现通过点击标题切换字体

    这个主要通过判断被点击的元素的子元素中是否包含了b元素来进行字体的切换,其中wrapInner函数是为了在$author元素的内部添加b标签. 切换回正常字体是通过将内容转化为纯文本形式,再替换元素内 ...

  7. 在Ueditor / Umeditor中实现上传图片跨域

    近几天公司的后台管理需要图文编辑文章,但是ueditor提供的方法中,本地图片的上传是通过flash的方式处理的,且不支持跨域.若要在已经前后端分离的Angular项目中使用,需要做复杂的环境配置.跟 ...

  8. final的用法

    先来看一段代码 class Car extends Vehicle {     public static void main (String[] args)     {         new  C ...

  9. Android WebView导入HTML使Js生效的方法

    WebSettings ws = webview.getSettings(); ws.setJavaScriptEnabled(true);//加上这句 webview.loadDataWithBas ...

  10. Android自定义评分控件:RatingStarView

    RatingStarView Android自定义的评分控件,类似ProgressBar那样的,使用星星图标(full.half.empty)作为progress标识的评分/打分控件. 效果图 图1: ...