Spark是一个分布式系统,也是集多个功能模块于一身的统一平台。它基于一个内核模块衍生出机器学习,实时流计算,OLAP,和图数据处理等模块,如图1-1-1所示。本书主要介绍Spark内核模块的实现原理。
从图1-1-1中可以看出Spark内核模块是基础层,它是所有上层功能模块的基础。所有上层的功能模块都使用Spark内核模块提供的接口来实现其功能。
Spark应用可以在多种资源管理框架中执行,由于在Spark应用执行前,资源管理框架的各个服务已经启动(Local模式除外),所以,区分Spark应用执行时创建的服务和资源框架提供的服务非常重要。
图1-1-2 Spark应用执行架构
图1-1-2是Spark应用执行时的基本架构图。图中用两种颜色进行了标识,其中浅绿色的方块是资源管理框架的服务,比如:YARN模式的Resource Manager(对应Cluster Manager)和Node Manager(对应Worker节点);又比如,Standalone模式下的Master和Worker服务。要注意的是:这些服务在Spark应用程序提交和执行前就一直处于运行状态,Spark应用运行运行在资源管理平台之上,当执行Spark应用程序时,会向资源管理器(Cluster Manager)申请运行资源。
而淡蓝色的模块是Spark应用执行时才启动的服务或对象,比如:Spark Session和Executor,当Spark应用执行完成后,这些服务或对象以及运行Spark应用所占用的资源就会被回收。
下表是图1-1-2中各个角色的简要说明,后面还会针对每个角色进行详细分析。
角色名 | 归属 | 说明 |
---|---|---|
Cluster Manager | 资源管理框架 | 负责为运行在资源管理跨框架上的应用程序分配资源。 |
Worker | 资源管理框架 | 根据Cluster Manager的指令分配资源,执行应用程序,释放资源。 |
Driver | Spark应用 | Spark应用程序的Driver端,Spark应用由Driver驱动执行。 |
Executor | Spark应用 | Spark应用程序的执行端,在执行Spark应用时创建,执行完成后被回收。 |
SparkSession | Spark应用 | Spark应用的核心元素,通过它来初始化Spark应用的环境。在Spark应用执行时创建。执行完成后关闭。 |
随着分布式应用越来越多,为了更好的利用和管理CPU和内存资源,诞生了许多分布式资源管理框架。通过这些资源管理框架可以更好的为分布式应用分配和管理计算资源。比如:Hadoop使用了YARN作为资源管理框架,另外还诞生了开源的Mesos等;随着云计算的发展,诞生了Kubernetes等。
Spark也支持了这几种主流的资源管理框架,Spark应用可以运行在这几种资源管理框架之上。另外,Spark还自己实现了一套资源管理框架。在执行Spark应用时,可以指定使用哪种资源管理框架。
表1-1-1是各种资源管理框架的角色和Spark应用执行架构中角色的对应关系表。由于Spark对Kubernetes的支持还处于试验阶段,所以,这里暂不介绍。
资源管理框架 | Cluster Manager | Worker |
---|---|---|
Spark自带 | Master服务 | Worker服务 |
Yarn | Resouce Manager服务 | Node Manager服务 |
Mesos | Mesos master服务 | Mesos Agent服务 |
表1-1-1 资源管理框架和Spark架构角色对应关系
Spark应用(Application)是用户基于Spark编写的应用程序。它由一个Driver进程和一组Executor进程组成,它们都是在Spark应用执行时创建,应用执行完成后即被回收。Spark应用可以使用多种语言编写:python,R,java,scala等。
Spark应用可以运行在图1-1-1所描述的三种资源管理框架中,也可以以Local模式运行。在Local模式下Driver和Executor都在同一个JVM中运行,Local模式的详细运行原理在“Local模式”一节中进行分析。
Driver负责启动Spark应用,驱动应用的执行。它对Spark应用的整个执行过程进行管控,它是Spark应用程序的"master"(注意不要和资源管理框架的master混淆。这样称呼主要因为:在Spark应用执行时,Driver端会启动很多服务的master端,这些服务的slave端运行在Executor上,这些服务的slave会向Driver端对应的master注册或汇报运行状态信息)。
Driver通过运行Spark应用的main函数来执行应用,从应用开始执行到把Task提交到Executor端有很多步骤,这些步骤都在Driver端完成。Driver端会把应用进行编排,最终会生成一个个的Task,并提交给Executor去执行,当Task被提交到Executor端后,Driver会和Executor进行通信,实时监控执行状态。总的来说Driver需要完成以下几个方面的工作:
注意,Driver也可以运行在资源管理框架的某个节点上(例如:yarn cluster模式下运行在Application Master中 )。
由于Driver端是Spark应用执行的核心,有时候我们需要对其资源进行设置,可以通过以下方式来对Driver的内存和CPU个数进行设置:
1)Executor的创建
Executor是在执行Spark应用时由执行后台服务创建。创建多少个Executor,每个Executor使用多少内存和CPU都可以通过参数进行指定。但注意:Local运行模式和其他模式不同,Local模式下只会创建一个Executor。
2)Executor的职责
Executor是执行Spark应用的容器,顾名思义,它的职责就是根据Driver端的要求来启动执行线程,执行任务,并返回执行结果。
3)Executor的资源配置
每次执行Spark任务时,都可以对Executor的个数,每个Executor的CPU和内存大小等进行配置。Executor的这些配置非常关键,会直接影响Spark任务的执行效率。
Job是Spark应用执行层次结构中的最高层元素。我们知道,Spark应用的通过Driver端的程序来驱动应用的执行,在Spark应用程序中,每个RDD的Action操作都对应一个Job。
每个Job会划分成一系列的Stage, Stage的数量依赖于发生过多少次shuffle操作。
每个Job都被划分为一些较小的任务集(Task Set),这些任务集称为Stage。这些Stage相互依赖,从而形成一个Stage的DAG图(有向无环图)。
发送给Executor端执行的工作单元。每个RDD的分区对应一个Task,也就是说,触发任务执行的RDD有多少个分区就会创建多少个Task。Task的创建是在Driver端完成,而Task的执行在Executor端。Executor会创建一个线程池来执行Task,每个Task对应一个执行线程。
SparkSession是Spark SQL的入口点。它是您在开发Spark SQL应用程序时首先要创建的对象之一。在创建SparkSession时,会同时创建SparkContext和SparkEnv。
在SparkSession中提供了多种创建Dataset和DataFrame的方法。
该进程运行应用程序的main()函数并创建SparkContext。在创建SparkContext时,会为Spark应用准备执行环境,包括各种服务的初始化,各种工具类的创建等。
在创建SparkContext时会和ResourceManager通信来申请任务执行资源,当SparkContext创建完成时Spark应用的执行环境就准备完成了,包括:Driver端完成各种初始化服务的启动,Worker端的完成Executor的创建。
Spark提供了3种编程接口:RDD,Dataframe,SparkSql,三者的区别与联系如下表:
RDD | DataFrame | Sparksql | |
---|---|---|---|
不可变 | Yes | Yes | Yes |
表结构 | No | Yes | Yes |
Spark V1 | Yes | Yes | Yes |
Spark V2 | Yes | Yes | Yes |
性能优化 | No | Yes | Yes |
数据类型支持 | Yes | No | Yes |
语法错误检查 | 编译时 | 编译时 | 编译时 |
错误分析 | 编译时 | 运行时 | 编译时 |
语言支持 | 都支持 | Python,java,scala,R |
从上表我们可以看出:
1)DataFrame和SparkSql是带有schema的结构化数据结构,因为有了Schema,所以会让数据更容易理解和使用。
2)DataFrame和SparkSql会对用户编写的代码进行优化,而Spark不会对RDD进行优化,因为它已经是最底层的基本元素了。实际上,在执行DataFrame和SparkSql任务时,会经过从逻辑执行计划到物理执行计划,再到RDD的代码生成等一系列的步骤,而这些步骤的目的就是为了得到RDD的最优化执行代码。
3)另外还要注意,DataFrame支持的语言是有限制的。
本文介绍了Spark的基本架构,和Spark应用的基本组成。通过本文可以对Spark有一个总体的概念,这对于理解其原理打下一个基础。