Spark Runtime里的主要层次分析,梳理Runtime组件和执行流程,
Job=多个stage,Stage=多个同种task, Task分为ShuffleMapTask和ResultTask,Dependency分为ShuffleDependency和NarrowDependency
面向stage的切分,切分依据为宽依赖
维护waiting jobs和active jobs,维护waiting stages、active stages和failed stages,以及与jobs的映射关系
主要职能
submitJob(rdd, ...)
或runJob(rdd, ...)
。在SparkContext
里会调用这两个方法。 TaskScheduler.submitTask(taskSet, ...)
方法,把Task描述提交给TaskScheduler。TaskScheduler依据资源量和触发分配条件,会为这个TaskSet分配资源并触发执行。DAGScheduler
提交job后,异步返回JobWaiter
对象,能够返回job运行状态,能够cancel job,执行成功后会处理并返回结果TaskCompletionEvent
Accumulator
加一,在job里为该task置true,job finish总数加一。加完后如果finish数目与partition数目相等,说明这个stage完成了,标记stage完成,从running stages里减去这个stage,做一些stage移除的清理工作 Accumulator
加一,在stage里加上一个output location,里面是一个MapStatus
类。MapStatus
是ShuffleMapTask
执行完成的返回,包含location信息和block size(可以选择压缩或未压缩)。同时检查该stage完成,向MapOutputTracker
注册本stage里的shuffleId和location信息。然后检查stage的output location里是否存在空,若存在空,说明一些task失败了,整个stage重新提交;否则,继续从waiting stages里提交下一个需要做的stageMapOutputTracker
注销掉。最后,如果这次fetch的blockManagerId对象不为空,做一次ExecutorLost
处理,下次shuffle会换在另一个executor上去执行。TaskScheduler
处理,如Exception, TaskResultLost, commitDenied等。其他职能
1. cacheLocations 和 preferLocation
private val cacheLocs = new HashMap[Int, Array[Seq[TaskLocation]]]
维护task和executor对应关系,executor和物理资源对应关系,在排队的task和正在跑的task。
内部维护一个任务队列,根据FIFO或Fair策略,调度任务。
TaskScheduler
本身是个接口,spark里只实现了一个TaskSchedulerImpl
,理论上任务调度可以定制。下面是TaskScheduler
的主要接口:
def start(): Unit
def postStartHook() { }
def stop(): Unit
def submitTasks(taskSet: TaskSet): Unit
def cancelTasks(stageId: Int, interruptThread: Boolean)
def setDAGScheduler(dagScheduler: DAGScheduler): Unit
def executorHeartbeatReceived(execId: String, taskMetrics: Array[(Long, TaskMetrics)],
blockManagerId: BlockManagerId): Boolean
主要职能
submitTasks(taskSet)
,接收DAGScheduler
提交来的tasks TaskSetManager
,添加到任务队列里。TaskSetManager
跟踪每个task的执行状况,维护了task的许多具体信息。TaskScheduler
对照手头的可用资源和Task队列,进行executor分配(考虑优先级、本地化等策略),符合条件的executor会被分配给TaskSetManager
。SchedulerBackend
,调用launchTask(tasks)
,触发executor上task的执行。task描述被序列化后发给executor,executor提取task信息,调用task的run()
方法执行计算。cancelTasks(stageId)
,取消一个stage的tasks SchedulerBackend
的killTask(taskId, executorId, ...)
方法。taskId和executorId在TaskScheduler
里一直维护着。resourceOffer(offers: Seq[Workers])
,这是非常重要的一个方法,调用者是SchedulerBacnend
,用途是底层资源SchedulerBackend
把空余的workers资源交给TaskScheduler
,让其根据调度策略为排队的任务分配合理的cpu和内存资源,然后把任务描述列表传回给SchedulerBackend
"spark.task.cpus=1"
TaskSetManager
的resourceOffer(execId, host, maxLocality)
方法里DAGScheduler
的taskStarted(task, info)
方法,通知DAGScheduler
,这时候每次会触发DAGScheduler
做一次submitMissingStage
的尝试,即stage的tasks都分配到了资源的话,马上会被提交执行statusUpdate(taskId, taskState, data)
,另一个非常重要的方法,调用者是SchedulerBacnend
,用途是SchedulerBacnend
会将task执行的状态汇报给TaskScheduler
做一些决定 TaskLost
,找到该task对应的executor,从active executor里移除,避免这个executor被分配到其他task继续失败下去。TaskResultGetter.enqueueSuccessfulTask(taskSet, tid, data)
,否则调用TaskResultGetter.enqueueFailedTask(taskSet, tid, state, data)
。TaskResultGetter
内部维护了一个线程池,负责异步fetch task执行结果并反序列化。默认开四个线程做这件事,可配参数"spark.resultGetter.threads"=4
。TaskResultGetter取task result的逻辑
blockManager.getRemoteBytes(blockId)
从远程获取。如果远程取回的数据是空的,那么会调用TaskScheduler.handleFailedTask
,告诉它这个任务是完成了的但是数据是丢失的。否则,取到数据之后会通知BlockManagerMaster
移除这个block信息,调用TaskScheduler.handleSuccessfulTask
,告诉它这个任务是执行成功的,并且把result data传回去。TaskScheduler.handleFailedTask
,告诉它这个任务失败了,理由是什么。在TaskScheduler
下层,用于对接不同的资源管理系统,SchedulerBackend
是个接口,需要实现的主要方法如下:
def start(): Unit
def stop(): Unit
def reviveOffers(): Unit // 重要方法:SchedulerBackend把自己手头上的可用资源交给TaskScheduler,TaskScheduler根据调度策略分配给排队的任务吗,返回一批可执行的任务描述,SchedulerBackend负责launchTask,即最终把task塞到了executor模型上,executor里的线程池会执行task的run()
def killTask(taskId: Long, executorId: String, interruptThread: Boolean): Unit =
throw new UnsupportedOperationException
粗粒度:进程常驻的模式,典型代表是standalone模式,mesos粗粒度模式,yarn
细粒度:mesos细粒度模式
这里讨论粗粒度模式,更好理解:CoarseGrainedSchedulerBackend
。
维护executor相关信息(包括executor的地址、通信端口、host、总核数,剩余核数),手头上executor有多少被注册使用了,有多少剩余,总共还有多少核是空的等等。
主要职能
RegisterExecutor(executorId, hostPort, cores, logUrls)
。这是executor添加的来源,通常worker拉起、重启会触发executor的注册。CoarseGrainedSchedulerBackend
把这些executor维护起来,更新内部的资源信息,比如总核数增加。最后调用一次makeOffer()
,即把手头资源丢给TaskScheduler
去分配一次,返回任务描述回来,把任务launch起来。这个makeOffer()
的调用会出现在任何与资源变化相关的事件中,下面会看到。StatusUpdate(executorId, taskId, state, data)
。task的状态回调。首先,调用TaskScheduler.statusUpdate
上报上去。然后,判断这个task是否执行结束了,结束了的话把executor上的freeCore加回去,调用一次makeOffer()
。ReviveOffers
。这个事件就是别人直接向SchedulerBackend
请求资源,直接调用makeOffer()
。KillTask(taskId, executorId, interruptThread)
。这个killTask的事件,会被发送给executor的actor,executor会处理KillTask
这个事件。StopExecutors
。通知每一个executor,处理StopExecutor
事件。RemoveExecutor(executorId, reason)
。从维护信息中,那这堆executor涉及的资源数减掉,然后调用TaskScheduler.executorLost()
方法,通知上层我这边有一批资源不能用了,你处理下吧。TaskScheduler
会继续把executorLost
的事件上报给DAGScheduler
,原因是DAGScheduler
关心shuffle任务的output location。DAGScheduler
会告诉BlockManager
这个executor不可用了,移走它,然后把所有的stage的shuffleOutput信息都遍历一遍,移走这个executor,并且把更新后的shuffleOutput信息注册到MapOutputTracker
上,最后清理下本地的CachedLocations
Map。reviveOffers()
方法的实现。直接调用了makeOffers()
方法,得到一批可执行的任务描述,调用launchTasks
。launchTasks(tasks: Seq[Seq[TaskDescription]])
方法。 LaunchTask(serializedTask)
事件。Executor是spark里的进程模型,可以套用到不同的资源管理系统上,与SchedulerBackend
配合使用。
内部有个线程池,有个running tasks map,有个actor,接收上面提到的由SchedulerBackend
发来的事件。
事件处理
launchTask
。根据task描述,生成一个TaskRunner
线程,丢尽running tasks map里,用线程池执行这个TaskRunner
killTask
。从running tasks map里拿出线程对象,调它的kill方法。全文完 :)
Spark Core Runtime分析: DAGScheduler, TaskScheduler, SchedulerBackend
原文地址:http://blog.csdn.net/pelick/article/details/44495611