文章目录
- 一 运行架构
- 1 运行架构
- 2 核心组件
- (1) Driver
- (2) Executor
- (3) Master & Worker
- (4) ApplicationMaster
- 3 核心概念
- (1) Executor与Core(核)
- (2) 并行度(Parallelism)
- (3) 有向无环图(DAG)
- 4 提交流程
- (1) Yarn Client模式
- (2) Yarn Cluster模式
- 二 RDD
- 1 什么是RDD
- 2 装饰者设计模式
- 3 RDD功能的组合
- 4 基础概念
- 5 核心配置属性
- 6 执行原理
- 7 RDD创建
- (1)从集合(内存)中创建RDD
- (2)从外部存储(文件)创建RDD
- (3)从其他RDD创建
- (4)直接创建RDD
一 运行架构
1 运行架构
Spark框架的核心是一个计算引擎,整体来说,它采用了标准 master-slave 的结构。
如下图所示,它展示了一个 Spark执行时的基本结构。图形中的Driver表示master,负责管理整个集群中的作业任务调度。图形中的Executor 则是 slave,负责实际执行任务。
2 核心组件
由上图可以看出,对于Spark框架有两个核心组件:
(1) Driver
Spark驱动器节点,用于执行Spark任务中的main方法,负责实际代码的执行工作。Driver在Spark作业执行时主要负责:
- 将用户程序转化为作业(job)
- 在Executor之间调度任务(task)
- 跟踪Executor的执行情况
- 通过UI展示查询运行情况
实际上,无法准确地描述Driver的定义,因为在整个的编程过程中没有看到任何有关Driver的字眼。所以简单理解,所谓的Driver就是驱使整个应用运行起来的程序,也称之为Driver类。
(2) Executor
Spark Executor是集群中工作节点(Worker)中的一个JVM进程,负责在 Spark 作业中运行具体任务(Task),任务彼此之间相互独立。Spark 应用启动时,Executor节点被同时启动,并且始终伴随着整个 Spark 应用的生命周期而存在。如果有Executor节点发生了故障或崩溃,Spark 应用也可以继续执行,会将出错节点上的任务调度到其他Executor节点上继续运行。
Executor有两个核心功能:
- 负责运行组成Spark应用的任务,并将结果返回给驱动器进程
- 它们通过自身的块管理器(Block Manager)为用户程序中要求缓存的 RDD 提供内存式存储。RDD 是直接缓存在Executor进程内的,因此任务可以在运行时充分利用缓存数据加速运算。
(3) Master & Worker
Spark集群的独立部署环境中,不需要依赖其他的资源调度框架,自身就实现了资源调度的功能,所以环境中还有其他两个核心组件:Master和Worker,这里的Master是一个进程,主要负责资源的调度和分配,并进行集群的监控等职责,类似于Yarn环境中的RM,,而Worker也是进程,一个Worker运行在集群中的一台服务器上,由Master分配资源对数据进行并行的处理和计算,类似于Yarn环境中NM。
(4) ApplicationMaster
Hadoop用户向YARN集群提交应用程序时,提交程序中应该包含ApplicationMaster,用于向资源调度器申请执行任务的资源容器Container,运行用户自己的程序任务job,监控整个任务的执行,跟踪整个任务的状态,处理任务失败等异常情况。
说的简单点就是,ResourceManager(资源)和Driver(计算)之间的解耦合靠的就是ApplicationMaster。
3 核心概念
(1) Executor与Core(核)
Spark Executor是集群中运行在工作节点(Worker)中的一个JVM进程,是整个集群中的专门用于计算的节点。在提交应用中,可以提供参数指定计算节点的个数,以及对应的资源。这里的资源一般指的是工作节点Executor的内存大小和使用的虚拟CPU核(Core)数量。
应用程序相关启动参数如下:
名称 | 说明 |
–num-executors | 配置Executor的数量 |
–executor-memory | 配置每个Executor的内存大小 |
–executor-cores | 配置每个Executor的虚拟CPU core数量 |
(2) 并行度(Parallelism)
在分布式计算框架中一般都是多个任务同时执行,由于任务分布在不同的计算节点进行计算,所以能够真正地实现多任务并行执行,这里是并行,而不是并发。这里将整个集群并行执行任务的数量称之为并行度。一个作业到底并行度是多少取决于框架的默认配置。应用程序也可以在运行过程中动态修改。
(3) 有向无环图(DAG)
大数据计算引擎框架根据使用方式的不同一般会分为四类,其中第一类就是Hadoop所承载的MapReduce,它将计算分为两个阶段,分别为 Map阶段 和 Reduce阶段。对于上层应用来说,就不得不想方设法去拆分算法,甚至于不得不在上层应用实现多个 Job 的串联,以完成一个完整的算法,例如迭代计算。
由于这样的弊端,催生了支持 DAG 框架的产生。因此,支持 DAG 的框架被划分为第二代计算引擎。如 Tez 以及更上层的 Oozie。这里不去细究各种 DAG 实现之间的区别,不过对于当时的 Tez 和 Oozie 来说,大多还是批处理的任务。接下来就是以 Spark 为代表的第三代的计算引擎。第三代计算引擎的特点主要是 Job 内部的 DAG 支持(不跨越 Job),以及实时计算。
这里所谓的有向无环图,并不是真正意义的图形,而是由Spark程序直接映射成的数据流的高级抽象模型。简单理解就是将整个程序计算的执行过程用图形表示出来,这样更直观,更便于理解,可以用于表示程序的拓扑结构。
DAG(Directed Acyclic Graph)有向无环图是由点和线组成的拓扑图形,该图形具有方向,不会闭环。
4 提交流程
所谓的提交流程,其实就是开发人员根据需求写的应用程序通过Spark客户端提交给Spark运行环境执行计算的流程。在不同的部署环境中,这个提交过程基本相同,但是又有细微的区别,这里不进行详细的比较,但是因为国内工作中,将Spark引用部署到Yarn环境中会更多一些。
Spark应用程序提交到Yarn环境中执行的时候,一般会有两种部署执行的方式:Client和Cluster。两种模式主要区别在于:Driver程序的运行节点位置。
(1) Yarn Client模式
Client模式将用于监控和调度的Driver模块在客户端执行,而不是在Yarn中,所以一般用于测试。
- Driver在任务提交的本地机器上运行
- Driver启动后会和ResourceManager通讯申请启动ApplicationMaster
- ResourceManager分配container,在合适的NodeManager上启动ApplicationMaster,负责向ResourceManager申请Executor内存
- ResourceManager接到ApplicationMaster的资源申请后会分配container,然后ApplicationMaster在资源分配指定的NodeManager上启动Executor进程
- Executor进程启动后会向Driver反向注册,Executor全部注册完成后Driver开始执行main函数
- 之后执行到Action算子时,触发一个Job,并根据宽依赖开始划分stage,每个stage生成对应的TaskSet,之后将task分发到各个Executor上执行。
(2) Yarn Cluster模式
Cluster模式将用于监控和调度的Driver模块启动在Yarn集群资源中执行。一般应用于实际生产环境。
- 在YARN Cluster模式下,任务提交后会和ResourceManager通讯申请启动ApplicationMaster,
- 随后ResourceManager分配container,在合适的NodeManager上启动ApplicationMaster,此时的ApplicationMaster就是Driver。
- Driver启动后向ResourceManager申请Executor内存,ResourceManager接到ApplicationMaster的资源申请后会分配container,然后在合适的NodeManager上启动Executor进程
- Executor进程启动后会向Driver反向注册,Executor全部注册完成后Driver开始执行main函数,
- 之后执行到Action算子时,触发一个Job,并根据宽依赖开始划分stage,每个stage生成对应的TaskSet,之后将task分发到各个Executor上执行。
二 RDD
Spark计算框架为了能够进行高并发和高吞吐的数据处理,封装了三大数据结构,用于处理不同的应用场景。三大数据结构分别是:
- RDD : 弹性分布式数据集
- 累加器:分布式共享只写变量
- 广播变量:分布式共享只读变量
接下来一起看看这三大数据结构是如何在数据处理中使用的。
数据结构:组织和管理数据的方式,如数组,链表,HashMap并不是一种数据结构,它是将多种数据结构组合在一起形成的一个容器对象
1 什么是RDD
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据处理模型。代码中是一个抽象类,它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。
RDD计算模型更适合并行计算和重复使用。
实现并行计算条件:在内部将数据分开(分区),将各个分区的数据发送给多个节点(Executor)进行计算
实现重复使用条件:计算单一,功能简单,从java角度理解就是能抽取出通用方法
RDD中有适合并行计算的分区操作,并封装了最小的计算单元,目的是更适合重复使用
Spark的计算主要就是通过组合RDD的操作,完成业务需求
2 装饰者设计模式
将多个功能组合在一起完成最终的操作,称为装饰者设计模式,用于扩展功能
如:IO 输入输出
字节流,字符流
InputStream in = new FileInputStream("xxxxx");
int i = -1;
while( (i = in.read()) != -1){
sout(i);
}
in.close;
一个一个字节取数据,太慢了,为增加效率,增加一个缓冲区
InputStream in = new BufferedInputStream(
new FileInputStream("xxxxx")
);
int i = -1;
while( (i = in.read(1024)) != -1){
sout(i);
}
in.close;
这时效率增加,但是读取文件的功能并不是由BufferedInputStream提供,它只是为程序增加了一个缓冲区(Buffer),读取文件功能仍然由FileInputStream完成,将多个功能组合在一起完成最终的需求,这就是装饰者设计模式。
此时想要一行一行读取数据
需要注意Stream为字节流,Reader为字符流,两者不能直接进行转换,所以需要添加一个转换流InputStreamReader,“UTF-8”为两者的转换方式
Reader in = new BufferedReader(
new InputStreamReader(
new FileInputStream("xxxxx"),
"UTF-8"
)
);
String s = null;
while( (s = in.readLine()) != null){
sout(s);
}
in.close;
底层还是FileInputStream一个一个字节读取数据,这时多个字节通过InputStreamReader转换会生成一个字符,如“A,B,C”三个字节转换成一个字符“中”
这时就完成了功能的叠加
- FileInputStream:负责读文件
- InputStreamReader:将字节转换成字符
- BufferedReader:增加缓冲区,提升效率
3 RDD功能的组合
RDD的功能扩展和IO一样,也采用了装饰者设计模式,将不同的功能组合在一起完成业务需求
RDD中的collect方法类似于IO中的read方法,read方法不执行,上面的三个new不会执行,同样,collect方法不执行,collect方法以上代码也不会执行,只是告诉了程序如何组合上述功能
RDD不存储任何数据,只封装逻辑,每一层只做自己的事情
sc.textFile("data/word.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
4 基础概念
- 弹性
- 存储的弹性:内存与磁盘的自动切换;
- 容错的弹性:数据丢失可以自动恢复;
- 计算的弹性:计算出错重试机制;
- 分片的弹性:可根据需要重新分片。
- 分布式:数据存储在大数据集群不同节点上
- 数据集:RDD封装了计算逻辑,并不保存数据,只封装逻辑
RDD[String] 并不是RDD集合中存储String类型数据,而是RDD对String类型数据进行计算,是数据的处理类型而不是数据的存储类型
- 数据抽象:RDD是一个抽象类,需要子类具体实现
- 不可变:RDD封装了计算逻辑,是不可以改变的,想要改变,只能产生新的RDD,在新的RDD里面封装计算逻辑
类比于不可变集合
- 可分区、并行计算
5 核心配置属性
- 分区列表
RDD数据结构中存在分区列表,用于执行任务时并行计算,是实现分布式计算的重要属性。
- 分区计算函数
Spark在计算时,是使用分区函数对每一个分区进行计算
- RDD之间的依赖关系
RDD是计算模型的封装,当需求中需要将多个计算模型进行组合时,就需要将多个RDD建立依赖关系
- 分区器(可选)
当数据为KV类型数据时,可以通过设定分区器自定义数据的分区
- 首选位置(可选)
计算数据时,可以根据计算节点的状态选择不同的节点位置进行计算
数据和计算的位置称为本地化
数据和计算的位置在同一个进程上称为进程本地化
数据和计算的位置在同一个节点上称为节点本地化
数据和计算的位置在同一个机架上称为机架本地化
移动数据不如移动计算
6 执行原理
从计算的角度来讲,数据处理过程中需要计算资源(内存 & CPU)和计算模型(逻辑)。执行时,需要将计算资源和计算模型进行协调和整合。
Spark框架在执行时,先申请资源,然后将应用程序的数据处理逻辑分解成一个一个的计算任务。然后将任务发到已经分配资源的计算节点上, 按照指定的计算模型进行数据计算,最后得到计算结果。
RDD是Spark框架中用于数据处理的核心模型,接下来我们看看,在Yarn环境中,RDD的工作原理:
- 启动Yarn集群环境
- Spark通过申请资源创建调度节点和计算节点
- Spark框架根据需求将计算逻辑根据分区划分成不同的任务
- 调度节点将任务根据计算节点状态发送到对应的计算节点进行计算
从以上流程可以看出RDD在整个流程中主要用于将逻辑进行封装,并生成Task发送给Executor节点执行计,接下来看看Spark框架中RDD是具体是如何进行数据处理的。
7 RDD创建
在Spark中创建RDD的创建方式可以分为四种:
(1)从集合(内存)中创建RDD
从集合中创建RDD,Spark主要提供了两个方法:parallelize和makeRDD
parallelize用于构建RDD,也可以将这个集合当成数据模型处理的数据源
parallelize单词表示并行
val sparkConf =
new SparkConf().setMaster("local[*]").setAppName("spark")
val sparkContext = new SparkContext(sparkConf)
val rdd1 = sparkContext.parallelize(
List(1,2,3,4)
)
val rdd2 = sparkContext.makeRDD(
List(1,2,3,4)
)
rdd1.collect().foreach(println)
rdd2.collect().foreach(println)
sparkContext.stop()
从底层代码实现来讲,makeRDD方法其实就是parallelize方法,makeRDD方法定义如下
def makeRDD[T: ClassTag](
seq: Seq[T],
numSlices: Int = defaultParallelism): RDD[T] = withScope {
parallelize(seq, numSlices)
}
(2)从外部存储(文件)创建RDD
由外部存储系统的数据集创建RDD包括:本地的文件系统,所有Hadoop支持的数据集,比如HDFS、HBase等。
val sparkConf =
new SparkConf().setMaster("local[*]").setAppName("spark")
val sparkContext = new SparkContext(sparkConf)
val fileRDD: RDD[String] = sparkContext.textFile("data/word.txt")
fileRDD.collect().foreach(println)
sparkContext.stop()
textFile方法可以通过路径获取数据,所以可以将文件作为数据处理的数据源
textFile路径可以是相对路径,也可以是绝对路径,甚至可以是分布式存储HDFS路径
textFile路径不仅仅可以是文件路径,也可以是目录路径,如果是目录路径,会将路径内的所有文件全部读取过来
textFile路径也可以是通配符路径
val fileRDD: RDD[String] = sparkContext.textFile("data/word*.txt")
如果在读取文件后,想要获取文件数据的来源,使用wholeTextFiles方法,第一个值为文件路径,第二个值为文件内容
val rdd: RDD[(String, String)] = sparkContext.wholeTextFiles("data/world*.txt")
(3)从其他RDD创建
主要是通过一个RDD运算完后,再产生新的RDD。
(4)直接创建RDD
使用new的方式直接构造RDD,一般由Spark框架自身使用。