浅谈 Spark 分布式计算
通过之前的文章《Spark RDD分区数与分区器源码解析》我们了解到了什么是 RDD,RDD 可以简单理解为弹性分布式数据集,RDD 提供了很多种类的算子,那么使用 RDD 的这些算子是如何在分布式环境下去执行的呢?
归根结底,我们编写的代码是需要通过 Spark 去帮助我们把任务分布到各个集群上并执行的,那么这一类过程我们统称为分布式计算,Spark 能实现分布式计算功能主要是靠它的进程模型以及调度系统。
一、Spark 进程模型
Spark 进程模型一共分为两块,分别是:
Driver:负责解析用户代码、构建计算流图,并将计算流程拆分为分布式计算任务然后提交给集群去运行,本质上就是一个运行着上述代码逻辑的 JVM 进程,当我们提交了一个 Spark 应用之后,便会启动一个 Driver 进程。
Executor:真正负责执行用户代码的进程,在 Spark 分布式环境里,一个工作节点上会运行多个 Executor,然后由多个 Executor 同时去执行任务代码。
它们之间的关系类似于这样:
那么大家可能会好奇,Driver 具体是如何解析用户代码并构建计算流图、Executor 又是如何执行任务的呢?就让我们带着这些问题,具体来看一下 Spark 的调度系统。
二、Spark 调度系统
前面我们讲到 Driver 是一个进程,那么这个进程里面还运行着 DAGScheduler、SchedulerBackend 以及 TaskScheduler 三个线程,这三个线程各自有各自的功能。
DAGScheduler
DAGScheduler 的角色有点类似于架构师,负责将整体系统的架构拆分为一个又一个的项目然后交给其他人去实现,这个整体系统架构就是 DAG,DAG 全称 Directed AcyclicGraph —— 有向无环图。在 Spark 中用 DAG 来描述我们的计算逻辑,DAG 是一组顶点和边的组合,在 Spark 中顶点代表了 RDD, 边代表了对 RDD 的一系列操作。在 Spark 中引入 DAG 可以优化计算计划,比如减少 Shuffle 数据。
那么 DAGScheduler 的职责就是将计算图 DAG 以 Shuffle 为边界拆分为执行阶段 Stages,同时将这个 Stage 转换为 TaskScheduler 所需要的 Task 去调度。
SchedulerBackend
SchedulerBackend 的角色则有点类似于人力总监,负责实时汇总并掌握全公司的人力,全公司的人力对应的就是 Spark 的计算资源,核心职责就是实时收集集群中可用的计算资源并提供给 TaskScheduler 用以调度。
这些计算资源都是通过与集群内所有 Executor 中的 ExecutorBackend 保持周期性通信来获取到的,同时内部使用 ExecutorDataMap 来保存这些集群信息,ExecutorDataMap 是一种 HashMap,它的 Key 是每个 Executor 的字符串,Value 则是用于封装 Executor 的一个对象,这个对象里面会记录 RPC 地址、主机地址、可用 CPU 核数等,它相当于是对 Executor 做的一个描述,描述该 Executor 的资源有哪些。
同时 SchedulerBackend 会将可调度的资源封装成一个 WorkerOffer 提供给 TaskScheduler 计算资源,WorkerOffer 封装了 ExecutorID、主机地址、CPU 核数,用来表示一份可用于调度任务的空闲资源。
TaskScheduler
TaskScheduler 从 DAGScheduler 获取到的每一个任务本身都具有不同的资源分配规则,比如说不同 Stages 之间,TaskScheduler 提供了两种调度模式,FIFO(先到先得)和 FAIR(公平调度),而在同一个 Stages 内的不同任务可以按照本地性级别来分配 Executor 执行,从 PROCESS_LOCAL(同一进程内)、NODE_LOCAL(同一节点内)、RACK_LOCAL(同一物理机内)到 ANY(无限制),限制越来越宽松。
TaskScheduler 的角色则类似于项目经理,总架构师拆分项目后分配给 TaskScheduler,然后 TaskScheduler 又从人力资源总监 SchedulerBackend 那里获取到自己可用的组员名单,然后将自己拿到的任务以及名单按照资源来分配规则,安排好每个员工的具体工作之后,再给到 SchedulerBackend,SchedulerBackend 在将这些任务进一步分配给分公司的人力资源管理 ExecutorBackend。
ExecutorBackend
ExecutorBackend 作为分公司的人力资源主管,在拿到活之后,会把任务分配给底下的组员,这些组员就是 Executors 线程池里的一个又一个 CPU 线程,每个线程负责处理一个 Task,处理完之后这些线程便会通过 ExecutorBackend 告知 SchedulerBackend,SchedulerBackend 和 TaskScheduler 再以接力的方式告知 DAGScheduler,后续再开启下一轮的 Stages 计算。
总结
在介绍完 Spark 的进程模型以及任务调度系统之后,想必大家对于 Spark 又有了一个新的认识,掌握了这些内容就等于掌握了 Spark 分布式计算的精髓,为我们今后开发高性能代码打下了良好的基础。但这只是冰山一角,Spark 本身是一个非常庞大的架构,它衍生出了很多框架,每个框架都有不同的设计理念,都值得我们去学习,学无止境,让我们一起学好基础,努力提高自己吧。
注:文章部分参考来源于网络,如有侵权,请联系删除!
本期内容就到这里了,如果喜欢就点个关注吧,微信公众号搜索“数 新 网 络 科 技 号”可查看更多精彩内容~
版权声明: 本文为 InfoQ 作者【数新网络官方账号】的原创文章。
原文链接:【http://xie.infoq.cn/article/2178579262336d2c367e7cf30】。文章转载请联系作者。
评论