文章目录
1. Spark任务调度概述
当Driver任务启动之后,Driver则会根据用户程序逻辑准备任务,并根据Executor资源情况逐步分发任务。
一个Spark应用程序包括Job、Stage以及Task三个概念:
- Job: 以Action算子为界,遇到一个Action算子就触发一个Job;
- Stage: 是Job的子集,以RDD宽依赖(即Shuffle)为界,遇到Shuffle做一次划分;
- Task: 是Stage的子集,以并行度(分区数)来衡量,分区数是多少,则有多少Task
2. Spark通信机制
(待补充)
3. 任务调度过程
Spark任务调度分为两种粒度:
- Stage级的调度
- Task级的调度
DAGScheduler: 负责Stage级的调度,将Job切分成若干Stage,并将每个Stage打包成TaskSet交给TaskScheduler调度。
TaskScheduler: 负责Task级的调度,将TaskSet按照指定的调度策略分发到Executor上执行,调度过程中SchedulerBackend
负责提供可用资源(SchedulerBackend
有多种实现,对应不同的资源管理系统)
Spark RDD通过其 Transactions(转换)
操作,形成RDD血缘关系图(DAG),最后通过Action(行动)
的调用,触发Job并调度执行。
下图描述 Spark-On-YARN
模式下在任务调度期间,ApplicationMaster、Driver、Executor内部模块的交互过程
3.1 Spark Stage 级调度
Spark的任务调度是从DAG切割开始,主要是由DAGScheduler
来完成。当遇到一个Action算子操作后就会触发一个Job的计算,并交给DAGScheduler
来提交,下图是涉及到Job提交的相关方法调用流程图(按箭头顺序):
- 一个Stage是否被提交,需要判断它的父Stage是否执行,只有在父Stage执行完毕才能提交当前Stage;如果当前Stage没有父Stage,则从当前Stage开始提交。
3.2 Spark Task 级调度
Spark Task的调度是由TaskScheduler
来完成,前面的处理流程中,DAGScheduler
将Stage打包到 TaskSet 交给TaskScheduler
, TaskScheduler
会将 TaskSet 封装为TaskSetManager
加入到调度队列中,TaskSetManager
结构如下所示:
TaskSetManager
负责监控管理同一个Stage中的Tasks,TaskScheduler
就是以TaskSetManager
为单元来调度任务
TaskScheduler
初始化后会启动SchedulerBackend
,它负责跟外界进行通讯,接收Executor的注册信息,并维护Executor的状态。同时它会定期的去询问TaskScheduler
,而TaskScheduler
在被询问时,会从调度队列中按照指定的调度策略选择TaskSetManager
去调度执行,Task调度流程大致如下图所示:
- 第一步是
TaskScheduler
将接收到的TaskSet封装为TaskSetManager
之后放入池子中 - 第二步就是从任务队列中按照相应的调度策略取出来在
SchedulerBackend
给过来的Executor上运行
3.2.1 调度策略
TaskScheduler
是以树的方式来管理任务队列的,树中的节点类型为Schedulable
,叶子节点为TaskSetManager
,非叶子节点为Pool
,下图是他们的继承关系:
- FIFO:先来后到
- FAIR: rootPool(根池)下面维持着多个子调度池,每个子调度池里面维护着一个调度队列
schedulableQueue
在FAIR模式中,需要先对子Pool进行排序,再对Pool里面的TaskSetManager
进行排序,因为Pool
和TaskSetManager
都继承自Schedulable
,所以二者可以采用相同的排序算法。
排序过程是基于Fair-share
来比较的,每个要排序的对象包含三个属性,runningTasks值、minShare值、weight值。
具体的比较规则如下(遵循从1->3的顺序):
runningTasks
比minShare
小的先执行minShare
使用率低的先执行(minShare
使用率:runningTasks
/minShare
,下同)weight
使用率低的先执行- 以上均相等,则比较名字
3.2.2 本地化调度
本地化调度: 从调度队列中取出TaskSetManager
后,遍历里面的tasks,本地化调度策略决定了取出的task给到哪个Executor去执行。
根据每个task的优先位置,确定task的Locality
级别,Locality
共分为五种,优先级从高到低:
名称 | 解析 |
---|---|
PROCESS_LOCAL | 进程本地化,task和数据在同一个Executor中,性能最好 |
NODE_LOCAL | 节点本地化,task和数据在同一个节点中,但不在同一个Executor中,数据需要在进程间进行传输 |
RACK_LOCAL | 机架本地化,task和数据在同一个机架的两个节点上,数据需要通过网络在节点之间进行传输 |
NO_PREF | 对于task来说,从哪里获取都一样,没有好坏之分 |
ANY | task和数据可以在集群的任何地方,而且不在一个机架中,性能最差 |
3.2.3 失败重试与黑名单机制
Task失败后的通知顺序:
Executor
SchedulerBackend
TaskScheduler
- 该task对应的
TaskSetManager
对于失败的Task,会记录它失败的次数,如果失败次数还没有超过最大重试次数,那么就把它放回待调度的Task池子中,否则整个Application会失败
在记录Task失败次数过程中,会记录它上一次失败所在Executor Id 和 Host,这样下次再调度该Task时,会使用黑名单机制,避免它被调度到上一次失败的节点上,起到一定的容错作用。