Application:我们自己的 Spark 程序。
TaskRunner:将我们编写的代码,也就是要执行的算子以及函数拷贝,反序列化,然后执行 task。
Task:task 有两种 ShuffleMapTask 和 ResultTask,只有最后一个 Stage 是 ResultTask,其余是 ShuffleMapTask。
1.submit 提交,在当前节点启动一个 Driver 进程
2 . Driver执行Application(Application总是先创建SparkContext)构造SparkContext在初始化的时候做的最重要的两件事情就是构建DAGScheduler和TaskScheduler
3.在构造出 TaskScheduler 的时候,TaskScheduler 会你负责连接 Master,并向Master 注册 Application。
4.Master 通知 Worker 启动 Executor;Master 接收到 Application 注册的请求后会使用自己的资源调度算在 Spark 集群的 Worker 上让 Worker 为这个 Application启动多个 Executor。(调度算法)
5. Executor 启动之后会反向注册到 TaskScheduler 上。当所有 Executor 都注册到Driver 上Driver 的 SparkContext 初始化结束,会继续执行我们的代码。
6.每执行到一个 Action 操作就会创建一个 job;并把 job 提交给 DAGScheduler。
7.DAGScheduler 会将 job 划分为多个 stage,然后每个 stage 创建一个 TaskSet(里面包含了很多 task)。并把每一个 Taskset 给 TaskScheduler。(stage 划分算法)
8.TaskScheduler 会把 TaskSet 里的每一个 Task 提交到 Executor 上运行(task 分配算法)
9.Executor 每收到一个 Task 都会用一个 TaskRunner 来封装 task,然后从线程池里取出一个线程来执行 task。所以最后整个 Spark 应用程序的执行及时 Stage 分批次作为 TaskSet 提交到Executor 执行。每个 Task 针对 RDD 的一个 partition 执行我们定义的算子和函数。
Master
1.主备切换机制
2.注册机制
Worker 的注册:
第一步 Worker 启动之后就会主动向 Master 进行注册
第二步过滤,将状态是 dead 的 Worker 过滤掉,对于状态为 unknown 的Worker,清理旧的 Worker 信息替换为新的 Worker 信息
第三步把 Worker 加入内存缓存中(HashMap)
第四步用持久化引擎将 Worker 信息进行持久化
第五步调用 scheduler()方法进行调度
Driver 的注册:
第一步用 Spark-submit 提交 Application 时首先就会注册 Driver
第二步把 Driver 信息加入内存缓存中(HashMap)
第三步加入等待调度队列(ArrayBuffer)
第四步用持久化引擎将 Worker 信息进行持久化
第五步调用 scheduler()方法进行调度
Application 的注册(很重要):
第一步 Driver 启动好之后会执行我们的 Application 代码,初始化SparkContext,底层的 SparkDeploySchedulerBackend 会向 Master 注册
第二步把 Application 信息加入内存缓存中(HashMap)
第三步加入等待调度 Application 队列(ArrayBuffer)
第四步用持久化引擎将 Application 信息进行持久化
第五步调用 scheduler()方法进行调度
3、状态改变处理机制源码分析
4、资源调度机制源码分析(schedule(),两种资源调度算法)
Application 的调度
spreadOutApps:每个可用 Worker 均分
非 spreadOutApps:尽可能少的 Worker 数量
DAGScheduler
Stage 划分算法:
从触发 Action 操作的那个 rdd 往前倒推,首先为最后一个 rdd 创建一个 stage,然后往前倒推,如果发现某个 rdd 是宽依赖,那么就会将宽依赖的那个 rdd 划分为一个新的 stage,然后以此类推,直到所有 rdd 遍历完为止。源代码归结为以下三部:
-
从 finalStage 倒推
-
通过宽依赖来进行新的 Stage 的划分
-
使用递归优先提交父 Stage
对于每一种有 shuffle 的操作,比如:groupByKey、reduceByKey、countByKey底层都对应了三个 RDD:MapPartitionsRDD、ShuffleRDD、MapPartitionsRDD,第一对应一个 Stage,后两个对应一个 Stage
Task 的最佳位置
Stage 从最后一个 RDD 开始找,哪个 RDD 的 Partition 被 cache 或者 checkpoint了,那么 task 的最佳位置就是 cache 或者 checkpoint 的位置。好处:这样 task在该节点执行,直接找缓存或者 checkpoint 的数据,不用再计算之前的 RDD 了。找完所有的 RDD 如果都没有缓存或者 checkpoint,那么久没有最佳位置,那么此task 就要遵循 TaskScheduler 的分配。
TaskScheduler
给每个 TaskSet 都会创建一个 TaskSetManager,TaskSetManager 会负责他的那个 TaskSet 的运行状况的监视和管理。
Task 的分配:Spark 用本地化级别这种模型去优化 Task 的分配和启动,优先希望在最佳本地化的地方启动 Task
PROCESS_LOCAL:进程本地化,代码和数据在同一个进程中,也就是在同一个 executor 中;计算数据的 task 由 executor 执行,数据在 executor 的 BlockManager中;性能最好NODE_LOCAL:节点本地化,代码和数据在同一个节点中;比如说,数据作为一个 HDFS block 块,就在节点上,而 task 在节点上某个 executor 中运行;或者是,数据和 task 在一个节点上的不同 executor 中;数据需要在进程间进行传输
NO_PREF:对于 task 来说,数据从哪里获取都一样,没有好坏之分
RACK_LOCAL:机架本地化,数据和 task 在一个机架的两个节点上;数据需要通过网络在节点之间进行传输
ANY:数据和 task 可能在集群中的任何地方,而且不在一个机架中,性能最差