主要部分包括:任务调度(Schedule),Shuffle机制,Executor,Task,BlockManager,DAG,ScheduleBackEnd,TasksetManager,
-
- Job运行流程
SparkContext的runJob提交—》 DagSchedule –》 dag.runJob -->
dag.handleJobSummitted --> 创建finalResultStage,然后submitStage(里面循环提交父stage)--》 dag.submitMissingTasks --> 判断是shuffleMapTask还是ResultTask,这是Spark的两种task类型 –》 生成taskSet --> taskScheduler.submitTasks提交执行,其中taskSchedule是taskScheduleImpl –》
taskScheduleImpl.submitTasks的流程:
createTaskSetManager –》 SchedulerBackend.reviveOffers() –》 通过CoarseGrainedSchedulerBackend(集群状态)
{
override def reviveOffers() {
driverEndpoint.send(ReviveOffers)
}
}
ReviveOffsers --> makeOffers() --> LauchTasks --> exectorEndpoint.send(LaunchTask)
通过driverEntPoint发送ReviveOffers到Exector执行。
具体执行:CoarseGrainedExecutorBackend
{
case LaunchTask(data) =>
if (executor == null) {
exitExecutor(1, "Received LaunchTask command but executor was null")
} else {
val taskDesc = TaskDescription.decode(data.value)
logInfo("Got assigned task " + taskDesc.taskId)
executor.launchTask(this, taskDesc)
}
}
-
- ScheduleBackEnd
典型的集群环境下的ScheduleBackEnd实现之一:CoarseGrainedScheduleBackEnd
首先看看CoarseGrained集群服务器之间的消息类型有哪些:
大体上可以分成两大类:Executor相关的消息和Task相关的消息。Executor相关的消息包括Executor的注册、删除、状态更新等。Task的消息包括LaunchTask,KillTask,状态更新等。集群间Task的调度和执行主要是通过ScheduleBackEnd来维护的。
除了CoarseGrainedScheduleBackEnd还有LocalScheduleBackEnd和StandaloneScheduleBackEnd等种类。
-
-
- Executor端
-
启动CoarseGrainedExecutorBackend,和主节点的ScheduleBackEnd通信。首先创建Driver端和主站联系,获取主站conf信息。然后创建SparkEnv。最后启动CoarseGrainedExecutorBackend消息处理主线程,接收ScheduleBackEnd的创建Task,关闭Exector等消息。
如果收到从ScheduleBackEnd来的注册成功消息(也就是RegisteredExecutor),则创建Executor,执行Task操作。CoarseGrainedExecutorBackend只是负责和ScheduleBackEnd之间的通信,并不是具体执行Task的类。
-
- DagSchedule
Dag调度类,对一个RDD进行shuffle分析,分解成多个Stage,从最后一个Stage逆向执行。Stage分成ResultStage和ShuffleMapStage两类。每个Stage根据分区分解成多个任务,用一个taskSetManager来管理。
DagSchedule用EventLoopProcess处理交互消息。有的消息时调用TaskScheduleImpl的方法;有的消息执行DagSchdule自己的私有方法。
-
-
- DAGSchedulerEvent消息类型
-
名称 | 说明 |
JobSubmitted | 创建finalResultStage,最后执行submitStage。 一定是最后一个stage,也就是ResultStage来触发job的提交,并创建ActiveJob对应它。 stage和他的父stage的jobId是同一个值。 |
MapStageSubmitted | 处理ShuffleMapStage,和jobSubmitted是对应的。 clearCacheLocs() |
StageCancelled | 对该Stage的每个job执行handleJobCancellation方法。 handleJobCancellation方法对job的每个stage,执行: { taskScheduler.cancelTasks(stageId, shouldInterruptThread) markStageAsFinished(stage, Some(failureReason)) } 从running Stages中删除,并通知listenerBus |
JobCancelled | 执行failJobAndIndependentStages 清除runningStage,调用TaskSchedule对应的消息处理,通知listenerBus等。 |
JobGroupCancelled | 批量处理JobCancelled |
AllJobsCancelled | 批量处理JobCancelled |
BeginEvent | 很简单: listenerBus.post(SparkListenerTaskStart(task.stageId, stageAttemptId, taskInfo)) |
GettingResultEvent | 很简单: listenerBus.post(SparkListenerTaskGettingResult(taskInfo)) |
CompletionEvent | task执行完成事件,根据完成的状态和结果来决定是否要重新提交,是否触发整个stage结束等状态更迭。 最后将事件发送给listenerBus。 这段代码比较长。 |
ExecutorAdded | Executor事件,新的Executor启动了。 从failedEpoch中删除该Executor |
ExecutorLost | Executor事件,Executor关闭了。 删除该Executor的blockManager信息,更新ShuffleStage的输出outputMapper信息,清除CacheLocs
|
TaskSetFailed | 对依赖Stage和Job执行failJobAndIndependentStages |
ResubmitFailedStages | 重新submitStage,针对已经失败的stage。 |
executorHeartbeatReceived | 通知blockmanager发送心跳,通知listenerBus |
-
- TaskScheduleImp
作为任务调度系统的重要类,(DagSchedule、TaskScheduleImpl、TaskSetManager)。
主要方法:
名称 | 说明 |
start | 启动backend 启动SpeculatableTasks |
executorLost | 删除executor 通知DagSchedule
|
submitTasks | 创建taskSetManager,并添加到ScheduleBuilder,等待下一步调度; backend的reviveOffers方法进行调度。
|
cancelTasks | 向backend发送KillTask消息 |
stop | 停止backend,停止taskResultGetter |
executorHeartbeatReceived | 更新matrics,通知dagschedule |
killTaskAttempt | backend发送KillTask消息。(backend向对应的executor发送KillTask消息) |
applicationId | 生成新的applicaionId,每个application对应一个TaskScheduleImpl。 |
|
|
其他方法(不是TaskSchedule接口中的方法):
名称 | 说明 |
resourceOffers | 对每一个taskSet,执行resourceOfferSingleTaskSet方法,直到不能找到满足条件的task为止: 搜索待执行的task。 backend会调用该方法获取待运行的task,根据本地化task优先级,获取指定本地化级别的task,最后生成待执行的task数组。 最后一步:提交执行task数组。 |
|
|
-
- CoarseGrainedSchedulerBackend
SchedulerBackend接口的一个实现。调度后台,负责集群间调度消息的传递。CoarseGrainedSchedulerBackend有一个DriverEndpoint,通过DriverEndPoint的receive方法接收消息,执行实际的消息处理。
主要方法:
名称 | 说明 |
start | 创建并启动DriverEndPoint |
stop | 停止DriverEndPoint |
makeOffers | (1)调用TaskScheduleImpl的resourcesOffer方法,从所有Executor中寻找可以分配的task数组。 (2)执行launchTask方法,向Executor发送LauchTask消息。 |
-
- CoarseGrainedExecutorBackend
Executor端,接收ScheduleEndpoint的消息,主要是LaunchTask消息。通过Executor执行。Executor启动时创建本地SparkEnv。
初始化参数:
driverUrl | driver端的连接地址 |
executorId | executor的编号,唯一 |
cores | Executor的cpu数量 |
核心示例代码:
override def receive: PartialFunction[Any, Unit] = {
case RegisteredExecutor =>
logInfo("Successfully registered with driver")
try {
executor = new Executor(executorId, hostname, env, userClassPath, isLocal = false)
} catch {
case NonFatal(e) =>
exitExecutor(1, "Unable to create executor due to " + e.getMessage, e)
}
case RegisterExecutorFailed(message) =>
exitExecutor(1, "Slave registration failed: " + message)
case LaunchTask(data) =>
if (executor == null) {
exitExecutor(1, "Received LaunchTask command but executor was null")
} else {
val taskDesc = TaskDescription.decode(data.value)
logInfo("Got assigned task " + taskDesc.taskId)
executor.launchTask(this, taskDesc)
}
case KillTask(taskId, _, interruptThread, reason) =>
if (executor == null) {
exitExecutor(1, "Received KillTask command but executor was null")
} else {
executor.killTask(taskId, interruptThread, reason)
}
case StopExecutor =>
stopping.set(true)
logInfo("Driver commanded a shutdown")
// Cannot shutdown here because an ack may need to be sent back to the caller. So send
// a message to self to actually do the shutdown.
self.send(Shutdown)
case Shutdown =>
stopping.set(true)
new Thread("CoarseGrainedExecutorBackend-stop-executor") {
override def run(): Unit = {
// executor.stop() will call `SparkEnv.stop()` which waits until RpcEnv stops totally.
// However, if `executor.stop()` runs in some thread of RpcEnv, RpcEnv won't be able to
// stop until `executor.stop()` returns, which becomes a dead-lock (See SPARK-14180).
// Therefore, we put this line in a new thread.
executor.stop()
}
}.start()
}