一、总体框架结构图
由上图我们可以看到Spark应用程序架构主要由Driver Program和Executor构成,Driver负责运行main()和创建SparkContext,Executor主要负责执行Task任务。
二、各大重要组件和概念
2.1 重要概念
概念 | 功能 |
---|---|
Application | 用户定义的Spark应用程序,用户提交后,Spark会为应用分配资源,调度和执行相关任务 |
Job | - 一个App中可以包含多个Job,每个Job都是由一个RDD Graph转化而来的 - 由Action算子触发 |
Stage | - 每个Job会根据RDD之间的宽依赖(Shuffle Dependency)来划分成多个Stage,每一个Stage中包含一组Task(也就是TaskSet) - 每个Stage中的Task类型都是相同的 |
Task | - 一个分区对应一个Task - Task执行RDD中对应的算子,Task被封装TaskRunner后放入Executor的线程中来执行,并由TaskRunner来进行调度 - Task类型有ShuffleMapTask和ResultTask |
Worker | 可以运行App代码的节点,如Yarn中NodeManager |
DAGScheduler | 用来构建基于Stage的有向无环图,划分的依据是根据RDD之间的宽依赖 |
TaskScheduler | - 将TaskSet提交给Executor去运行 - 维护TaskSet的运行状态标签,负责Task的失效重执行 |
2.2 相关组件
组件 | 功能 |
---|---|
Driver | - 运行Application的main函数并创建SparkContext - SparkContext的作用就是与Cluster Manager通讯,进行资源申请、任务的调度等。 |
Executor | - 某个App运行在Worker上的一个进程,用于执行App的Task - 每个App都有独立的Executor,Executor上运行的Task取决于分配的core数 |
Cluster Manager | 资源调度服务,用于资源的分配和调度,有standalone、mesos和yarn三种模式 |
三、应用的提交执行流程
3.1 运行模式
1)Spark应用的运行模式有以下几种:
- local[n]:本地模式
- standalone:使用spark内部的资源管理器,master-slaves架构
- mesos : Apache下的一个资源管理框架
- yarn : Hadoop下的一个资源管理框架
2)根据Driver的运行位置来划分有以下两种运行模式:
- Cluster:Driver和Executor运行在集群中的Worker上
- Client:Driver运行在客户端上,Executor运行在Worker上
3)命令行
- spark-shell:bin/spark-shell --master yarn --deploy-mode client(yarn的client模式)
- spark-submit:spark-shell底层也是调用spark-submit,如果你想知道它有什么参数,可以直接在spark目录下输入 spark-submit来查看
3.2 流程
这里我们就以 Spark On Yarn 为例来说明
3.2.1 Yarn-Client 模式
流程如下:
- 在客户端通过spark-submit向Yarn提交Application
- 应用在Client启动Driver,创建SparkContext并进行初始化
- ResourceManger为应用申请一个Container来启动Application Master,用来与Client中的SparkContext进行通讯。
- Application Master即承担起了为应用申请资源运行Executor的责任,一旦申请到资源,那么就会在指定的NodeManager的Container上创建Executor进程,Executor会向SparkContext注册,保持通讯
- SparkContext会根据任务所需资源,数据本地性等条件指定Task的运行在哪个Executor上,将Task分发到指定Executor并开始执行,Executor会周期向SparkContext汇报任务运行情况
- 一旦所有任务执行完毕,SparkContext会向ResourceManger申请注销自己并关闭
3.2.2 Yarn-Cluster 模式
流程如下:
- 在客户端通过spark-submit向Yarn提交Application
- ResourceManger为应用申请一个Container来启动Application Master,并且在Application Master进行SparkContext等初始化。
- Application Master即承担起了为应用申请资源运行Executor的责任,一旦申请到资源,那么就会在指定的NodeManager的Container上创建Executor进程,Executor会向SparkContext注册,保持通讯
- Applicaiton Master会根据任务所需资源,数据本地性等条件指定Task的运行在哪个Executor上,将Task分发到指定Executor并开始执行,Executor会周期向Applicaiton Master汇报任务运行情况
- 一旦所有任务执行完毕,Applicaiton Master会向ResourceManger申请注销自己并关闭
3.3 Job的全生命周期
- action算子触发
- 内部实际就是sc.runJob,最终调用了submitJob
- submit中最终调用了DAGScheduler的submitStage,由最后一个stage往前找,直到parentStage缺失调用submitMissingTask
- DAGScheduler将TaskSet提交给TaskScheduler执行
- TaskScheduler中实际调用了submit方法,而该方法是使用远程调用的方式来调用CoraseGraintedExecutorBackend的launchTask方法
- launchTask则是将包装了Task的TadkRunner丢进线程池中去运行
四、执行原理
Spark应用程序以进程集合为单位在分布式集群上运行,通过driver程序的main方法创建的SparkContext对象与集群交互。
1、Spark通过SparkContext向Cluster manager(资源管理器)申请所需执行的资源(cpu、内存等)
2、Cluster manager分配应用程序执行需要的资源,在Worker节点上创建Executor
3、SparkContext 将程序代码(jar包或者python文件)和Task任务发送给Executor执行,并收集结果给Driver。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103800160.png)
Spark详细运行过程如下图
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103800162.png)
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103800165.png)
涉及的几个定义和详细的运行过程如下:
1、Application:Spark应用程序
指的是用户编写的Spark应用程序,包含了Driver功能代码和分布在集群中多个节点上运行的Executor代码。
Spark应用程序,由一个或多个作业JOB组成,如下图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103800166.png)
2、Driver:驱动程序
Spark中的Driver即运行上述Application的Main()函数并且创建SparkContext,其中创建SparkContext的目的是为了准备Spark应用程序的运行环境。在Spark中由SparkContext负责和ClusterManager通信,进行资源的申请、任务的分配和监控等;当Executor部分运行完毕后,Driver负责将SparkContext关闭。通常SparkContext代表Driver,如下图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103801168.png)
3、Cluster Manager:资源管理器
指的是在集群上获取资源的外部服务,常用的有:Standalone,Spark原生的资源管理器,由Master负责资源的分配;Haddop Yarn,由Yarn中的ResearchManager负责资源的分配;Messos,由Messos中的Messos Master负责资源管理。
4、Executor:执行器
Application运行在Worker节点上的一个进程,该进程负责运行Task,并且负责将数据存在内存或者磁盘上,每个Application都有各自独立的一批Executor,如下图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103801170.png)
5、Worker:计算节点
集群中任何可以运行Application代码的节点,类似于Yarn中的NodeManager节点。在Standalone模式中指的就是通过Slave文件配置的Worker节点,在Spark on Yarn模式中指的就是NodeManager节点,在Spark on Messos模式中指的就是Messos Slave节点,如下图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103801172.png)
6、DAGScheduler:有向无环图调度器
基于DAG划分Stage 并以TaskSet的形势提交Stage给TaskScheduler;负责将作业拆分成不同阶段的具有依赖关系的多批任务;最重要的任务之一就是:计算作业和任务的依赖关系,制定调度逻辑。在SparkContext初始化的过程中被实例化,一个SparkContext对应创建一个DAGScheduler。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103801174.png)
7、TaskScheduler:任务调度器
将Taskset提交给worker(集群)运行并回报结果;负责每个具体任务的实际物理调度。如图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103802177.png)
8、Job:作业
由一个或多个调度阶段所组成的一次计算作业;包含多个Task组成的并行计算,往往由Spark Action催生,一个JOB包含多个RDD及作用于相应RDD上的各种Operation。如图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103802178.png)
9、Stage:调度阶段
一个任务集对应的调度阶段;每个Job会被拆分很多组Task,每组任务被称为Stage,也可称TaskSet,一个作业分为多个阶段;Stage分成两种类型ShuffleMapStage、ResultStage。如图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103802184.png)
Application多个job多个Stage:Spark Application中可以因为不同的Action触发众多的job,一个Application中可以有很多的job,每个job是由一个或者多个Stage构成的,后面的Stage依赖于前面的Stage,也就是说只有前面依赖的Stage计算完毕后,后面的Stage才会运行。
划分依据:Stage划分的依据就是宽依赖,何时产生宽依赖,reduceByKey, groupByKey等算子,会导致宽依赖的产生。
核心算法:从后往前回溯,遇到窄依赖加入本stage,遇见宽依赖进行Stage切分。Spark内核会从触发Action操作的那个RDD开始从后往前推,首先会为最后一个RDD创建一个stage,然后继续倒推,如果发现对某个RDD是宽依赖,那么就会将宽依赖的那个RDD创建一个新的stage,那个RDD就是新的stage的最后一个RDD。然后依次类推,继续继续倒推,根据窄依赖或者宽依赖进行stage的划分,直到所有的RDD全部遍历完成为止。
将DAG划分为Stage剖析:如上图,从HDFS中读入数据生成3个不同的RDD,通过一系列transformation操作后再将计算结果保存回HDFS。可以看到这个DAG中只有join操作是一个宽依赖,Spark内核会以此为边界将其前后划分成不同的Stage. 同时我们可以注意到,在图中Stage2中,从map到union都是窄依赖,这两步操作可以形成一个流水线操作,通过map操作生成的partition可以不用等待整个RDD计算结束,而是继续进行union操作,这样大大提高了计算的效率。
10、TaskSet:任务集
由一组关联的,但相互之间没有Shuffle依赖关系的任务所组成的任务集。如图所示。
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103803194.png)
提示:
1)一个Stage创建一个TaskSet;
2)为Stage的每个Rdd分区创建一个Task,多个Task封装成TaskSet
11、Task:任务
被送到某个Executor上的工作任务;单个分区数据集上的最小处理流程单元(单个stage内部根据操作数据的分区数划分成多个task)。如图所示。
总体如图所示:
![\](https://www.2cto.com/uploadfile/Collfiles/20181009/20181009103803203.png)
五、Spark读写流程
5.1 写入流程
- RDD调用compute( )方法进行指定分区的写入。
- CacheManager中调用BlockManater判断数据是否已经写入,如果未写则写入。
- BlockManager中数据与其他节点同步。(与副本节点通讯)
- BlockManager根据存储级别写入指定的存储层。(存储级别决定了数据是存储在内存还是磁盘,或者是两者结合)
- BlockManager向主节点汇报存储状态。
5.2 读取流程
- RDD调用compute( )方法进行指定分区的读取。
- 调用BlockManager的get()进行读取
- 在本地同步读取数据块,首先看能否在内存读取数据块,如果不能读取,则看能否从Tachyon读取数据块,如果仍不能读取,则看能否从磁盘读取数据块。
- 如果本地没有数据,就进行远程读取:远程获取调用路径,然后getRemote调用doGetRemote,通过BlockManagerWorker.syncGetBlock从远程获取数据。
六、Spark的容错机制
分布式系统常用的容错机制
- 数据检查点:成本高,耗费资源
- 记录数据的更新:耗费资源较少
Spark所采用的容错机制是记录数据更新和数据检查点相结合的形式。原因如下:
- RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(即血统)记录下来,以便恢复丢失的分区。也就是说当子RDD分区丢失时,只要重新计算它对应的父RDD分区就可以进行恢复,这对于窄依赖来说不存在冗余计算。
- 宽依赖也就是Shuffle Dependency,也就是发生在shuffle阶段的依赖,如果子RDD分区丢失了,那么重新计算的代价就比较大,因为有多个父RDD分区对应该子RDD分区,那么重新计算时所有的父RDD分区都得重新计算,造成了计算冗余
以下情况需要添加检查点
- DAG中依赖线过长,如果重算,则开销太大
- 在Shuffle Dependency上做CheckPoint(检查点)获得的收益更大
在RDD计算中,通过检查点机制进行容错,传统做检查点有两种方式:通过冗余数据和日志记录更新操作。RDD中所采用的就是通过冗余数据来缓存数据,然后对已进行冗余
操作的RDD执行删除该祖先的RDD依赖
官方建议,做检查点的RDD做好先缓存在内存中,否则需要进行重新计算
七、Spark的Shuffle机制
Shuffle中文意思就是混洗,跟MapReduce中的Shuffle的思想是相同,就是数据的重新分区和组合
Spark的Shuffle是在stage的承接阶段完成的,前面的stage会根据后面stage的分区数来将数据按照一定的规则分成相应的bucket,然后写到磁盘上。后续的stage会从元数据那里获得指定数据的所在节点,将数据拉取本地做进一步的操作
Shuffle分为两个阶段
- Shuffle Write :Shuffle的第一步骤,就是将前一个stage中的数据写到磁盘,用于第二步的Fetch
- 主要是在ShuffleMapTask中执行(runTask)方法
- 如果设置了map端进行聚合的话,那么会先执行数据在map的合并,减少网络传输
- Consolidate Shuffle相比普通Shuffle的优势在:普通shuffle产生的Shuffle文件数为map任务数 x Reduce任务数,而Consolidate Shuffle的理论Shuffle文件数为Spark Core数 x Reducer数,而概念上C_Shuffle引入了文件数组,一个Bucket不再对应一个文件,而是对应文件中的一个segment
- Shuffle Fetch:通过本地或者远程抓取需要的数据到本节点进行计算,完成操作如保存数据到指定路径或者作为下一个Shuffle的Shuffle Write
Shuffle Aggregator并不会对所有情况下的数据进行排序,所以Aggregator分为不需要外排和需要外排两种方式
- 不需要外排:数据全部保存在内存当中,使用AppendOnlyMap来进行数据的存储,数据是来一个处理一个,更新到map中,所以reducer的内存必须足够大,能够存储该分区的所有key和count的值
- 需要外排:reduce类型的操作,内存没办法存放所有的key-value对,必须借助外部磁盘
原文:
https://www.jianshu.com/p/7575a0c1c73b
https://www.2cto.com/net/201810/781296.html