目录
一、Spark On Yarn两种部署方式
当我们通过spark-submit方式来提交Spark应用到Yarn或者Spark集群的时候, 提供了两种部署模式: client 和 cluster。client模式 和 cluster模式本质区别: 【Driver程序运行的地方不同】
Client模式: 【Driver运行在提交应用程序的节点】,应用是在哪里进行提交的,Driver就会运行在哪里。默认值
优势: 测试比较方便,直接通过Driver看到最终返回的结果
弊端: 由于不是在一起的(Driver 和 executor不是在一个环境中),受网络影响因素会比较大,导致执行效率比较低
使用: 【一般用于开发和测试】Cluster模式: 【Driver运行在集群中某个Worker从节点中】
优势: 提升Driver程序和executor程序之间的传输效率,从而提升整体的运行效率
弊端: 由于Driver运行在集群里面,导致我们无法直接看到运行的结果,如果想要看到结果,就必须查看运行的日志
使用: 【推荐在生产环境使用】
二、spark-submit命令
后续需要将自己编写的Spark程序提交到相关的资源平台上,比如说: local yarn spark集群
Spark为了方便任务的提交操作,专门提供了一个用于进行任务提交的脚本文件: spark-submit
spark-submit在提交的过程中,设置非常多参数,调整任务相关信息。如果忘记了,可以使用spark-submit --help进行查看
- 基本参数设置
-
Driver的资源配置参数
- executor的资源配置参数
三、PySpark程序与Spark交互流程
1.client on Spark集群
1-在哪个节点上提交应用,就会在哪个节点上【启动Driver程序】
2-执行main函数,首先会创建SparkContext对象。底层是【基于PY4J】,根据python的构建方式,映射为Java代码
3-Driver程序连接到Spark集群的主节点Master。申请资源,用于启动Executor
4-Master主节点接收到资源申请,根据资源配置,分配资源。底层分配方案是【FIFO】。将分配方案返回给Driver程序
executor1:node1 2个cpu,2GB
executor2:node3 2个cpu,2GB
5-Driver程序连接到对应的Worker节点,占用相应资源,通知Worker启动Executor。启动以后,会反向注册回Driver程序6-Driver开始处理代码
6-1 Driver加载RDD相关的算子(API/函数/方法),根据算子间的依赖关系,绘制【DAG有向无环图和划分Stage阶段】,并且确定每个阶段要多少个线程,每个线程要分配给哪个Executor来执行。(任务分配)
6-2 Driver程序通知对应的Executor来执行具体任务
6-3 Executor接收到任务之后,开始执行。RDD的代码中,有大量的Python函数,Executor是JVM程序,无法执行Python函数。因此会【调用Python解释器】,得到返回结果,并且将结果返回给Executor
6-4 Executor在运行过程中,判断是否要将结果返回给Driver程序。如果需要,就直接返回;如果不需要,就直接输出,结束即可
6-5 Driver程序监控Executor的任务执行状态,如果所有的Executor都执行完成,那么就认为任务运行完成7-任务运行完后,Driver执行【sc.stop()】代码,通知Master。Master释放资源,Driver程序退出
2.cluster on Spark集群
区别点 : 【Driver程序就不是运行在提交程序的节点上了,而是在Spark集群中随机找一个从节点Worker启动Driver程序】
1-程序提交到Spark集群的主节点Master
2-主节点Master接收到任务之后,会根据Driver资源配置,在集群中随机选择某个从节点,占用相应资源,启动Driver程序
3-Driver程序执行main函数,首先会创建SparkContext对象。底层是基于PY4J,根据python的构建方式,映射为Java代码。创建成功后,会注册到Master主节点4-Driver程序连接到Spark集群的主节点。申请资源,用于启动Executor
5-Master主节点接收到资源申请,根据资源配置,分配资源。底层分配方案是FIFO先进先出。将分配方案返回给Driver程序
executor1:node1 2个cpu,2GB
executor2:node3 2个cpu,2GB
6-Driver程序连接到对应的Worker节点,占用相应资源,通知Worker启动Executor。启动以后,会反向注册回Driver程序7-Driver开始处理代码
7-1 Driver加载RDD相关的算子(API/函数),根据算子间的依赖关系,绘制DAG执行流程图和划分Stage阶段,并且确定每个阶段要多少个线程,每个线程要分配给哪个Executor来执行。(任务分配)
7-2 Driver程序通知对应的Executor来执行具体任务
7-3 Executor接收到任务之后,开始执行。RDD的代码中,有大量的Python函数,Executor是JVM程序,无法执行Python函数。因此会【调用Python解释器】,得到返回结果,并且将结果返回给Executor
7-4 Executor在运行过程中,判断是否要将结果返回给Driver程序。如果需要,就直接返回;如果不需要,就直接输出,结束即可
7-5 Driver程序监控Executor的任务执行状态,如果所有的Executor都执行完成,那么就认为任务运行完成8-任务运行完后,Driver执行sc.stop()代码,通知Master。Master释放资源,Driver程序退出
处使用的url网络请求的数据。
3.client on Yarn集群
区别点: 【Driver将资源申请的工作移交给了ApplicationMaster来负责,Driver只负责任务分配、任务管理等工作】
1- 首先会在提交的的节点上启动一个Driver程序
2- Driver启动后,执行mian函数,首先创建SparkContext对象(底层是基于PY4J,识别python的构建方式, 将其映射为Java代码)3- 连接Yarn集群的主节点ResourceManager,将需要申请的资源封装成一个任务,提交给Yarn的主节点ResourceManager。主节点ResourceManager收到任务后,首先随机的选择一个nodemanager启动ApplicationMaster
4- 当ApplicationMaster启动后,会和yarn的主节点建立心跳机制,告知已经启动成功了。启动成功后,就进行资源的申请工作,将需要申请的资源通过心跳包的形式发送给ResourceManager。ResourceManager收到资源申请后,开始进行资源分配工作,底层是基于资源调度器来实现(默认为Capacity容量调度)。当ResourceManager将资源分配OK后,等待ApplicationMaster进拉取操作。ApplicationMaster会定时的通过心跳的方式询问ResourceManager是否已经准备好资源,一旦发现准备好,立即拉取对应资源信息
5- ApplicationMaster根据获取到的资源信息连接对应的节点,通知对应nodemanager启动executor,并占用相应资源,nodemanage对应executor启动完成后,反向注册回给Driver程序(已经启动完成)
6- Driver开始处理代码:
6.1 首先会加载所有的RDD相关的API(算子),基于算子之间的依赖关系,形成DAG执行流程图,划分stage阶段,并且确定每个阶段应该运行多少个线程以及每个线程应该交给哪个executor来运行(任务分配)
6.2 Driver程序通知对应的executor程序,来执行具体的任务
6.3 Executor接收到任务信息后,启动线程,开始执行处理即可。executor在执行的时候,由于RDD代码中有大量的Python的函数,Executor是一个JVM程序,无法解析Python函数,此时会调用Python解析器,执行函数, 并将函数结果返回给Executor
6.4 Executor在运行过程中,如果发现最终的结果需要返回给Driver,直接返回Driver,如果不需要返回, 直接输出 结束即可
6.5 Driver程序监听这个executor执行的状态信息,当Executor都执行完成后,Driver认为任务运行完成了,同时ApplicationMaster也会接收到各个节点执行完成状态,然后通知ResourceManager。任务执行完成,ResourceManager回收资源,关闭ApplicationMaster,并通知Driver程序7- Driver执行sc.stop(),Driver程序退出即可
4.cluster on Yarn集群
区别点: 【Driver和ApplicationMaster合二为一,ApplicationMaster就是Driver,Driver就是ApplicationMaster】
1- 首先会将任务提交给Yarn集群的主节点(ResourceManager)
2- ResourceManager接收到任务信息后,根据Driver(ApplicationMaster)的资源配置信息要求,选择一个nodeManager节点(有资源的,如果都有随机)来启动Driver(ApplicationMaster)程序,并且占用相对应资源
3- Driver(ApplicationMaster)启动后,执行main函数。首先创建SparkContext对象(底层是基于PY4J,识别python的构建方式,将其映射为Java代码)。创建成功后,会向ResourceManager进行建立心跳机制,告知已经启动成功了4- 根据executor的资源配置要求,向ResourceManager通过心跳的方式申请资源,用于启动executor(提交的任务的时候,可以自定义资源信息)
5- ResourceManager接收到资源申请后,根据申请要求,进行分配资源。底层是基于资源调度器来资源分配(默认为Capacity容量调度)。然后将分配好的资源准备好,等待Driver(ApplicationMaster)拉取操作
executor1: node1 2个CPU 2GB内存
executor2: node3 2个CPU 2GB内存6- Driver(ApplicationMaster)会定时询问是否准备好资源,一旦准备好,立即获取。根据资源信息连接对应的节点,通知nodeManager启动executor,并占用相应资源。nodeManager对应的executor启动完成后,反向注册回给Driver(ApplicationMaster)程序(已经启动完成)
7- Driver(ApplicationMaster)开始处理代码:
7.1 首先会加载所有的RDD相关的API(算子),基于算子之间的依赖关系,形成DAG执行流程图,划分stage阶段,并且确定每个阶段应该运行多少个线程以及每个线程应该交给哪个executor来运行(任务分配)
7.2 Driver(ApplicationMaster)程序通知对应的executor程序, 来执行具体的任务
7.3 Executor接收到任务信息后, 启动线程, 开始执行处理即可: executor在执行的时候, 由于RDD代码中有大量的Python的函数,Executor是一个JVM程序 ,无法解析Python函数, 此时会调用Python解析器,执行函数, 并将函数结果返回给Executor
7.4 Executor在运行过程中,如果发现最终的结果需要返回给Driver(ApplicationMaster),直接返回Driver(ApplicationMaster),如果不需要返回,直接输出 结束即可
7.5 Driver(ApplicationMaster)程序监听这个executor执行的状态信息,当Executor都执行完成后,Driver(ApplicationMaster)认为任务运行完成了8- 当任务执行完成后,Driver执行sc.stop()通知ResourceManager执行完成,ResourceManager回收资源,Driver程序退出即可
四、RDD的基本介绍
1.什么是RDD
RDD:英文全称Resilient Distributed Dataset,叫做弹性分布式数据集,是Spark中最基本的数据抽象,代表一个不可变、可分区、里面的元素可并行计算的集合。
-
Resilient弹性:【RDD的数据可以保存到内存或者磁盘】
-
Distributed分布式:【RDD的数据可以分布式存储在集群中的节点,用于分布式计算】
-
Dataset数据集:【一个用于存放数据的集合】
2.RDD的五大特性
1、RDD是有一系列分区组成的
2、对RDD做计算,相当于对RDD的每个split或分区做计算
3、RDD之间存在着依赖关系,宽依赖和窄依赖
4、对于KV类型的RDD,我们可以进行自定义分区方案
5、移动数据不如移动计算,让计算程序离数据越近越好
3.RDD的五大特点
1、分区:RDD逻辑上是分区的,仅仅是定义分区的规则,并不是直接对数据进行分区操作,因为RDD本身不存储数据。
2、只读:RDD是只读的,要想改变RDD中的数据,只能在现有的RDD基础上创建新的RDD。
3、依赖:RDD之间存在着依赖关系,宽依赖和窄依赖
4、缓存:如果在应用程序中多次使用同一个RDD,可以将该RDD缓存起来,该RDD只有在第一次计算的时候会根据血缘关系得到分区的数据
5、checkpoint:与缓存类似的,都是可以将中间某一个RDD的结果保存起来,只不过checkpoint支持持久化保存
五、如何构建RDD
构建RDD对象的方式主要有两种:
1、通过 textFile(data): 通过读取外部文件的方式来初始化RDD对象,实际工作中经常使用。
2、通过 parallelize(data): 通过自定义列表的方式初始化RDD对象。(一般用于测试)
六、RDD分区数量如何确定
1- RDD分区数量(线程数量)一般设置为【CPU核数的2-3倍】
2- RDD的分区数量取决于多个因素: 调用任务时候设置CPU核数、对应API设置分区数量,以及本身读取文件的分区数等等因素
3- 当初始化SparkContext的时候, 其实我们确定了一个基本的并行度参数【spark.default.parallelism】,默认为CPU核数。如果是集群,至少为2;如果local模式,取决于local[N] N为多少,并行度就是多少,即分区数就是多少
4- 通过parallelize来构建RDD: 如果没有指定分区数,那么分区数量取决spark.default.parallelism;如果设置了分区数量,取决于设置的分区数
5- 通过textFile(path,minPartition)来构建RDD:
5-1 首先取决于defaultMinPartition的值。如果没有指定minPartition,defaultMinPartition等于min(spark.default.parallelism,2);如果指定了minPartition,defaultMinPartition等于minPartition
5-2 读取本地文件: 【RDD分区数=max(本地文件分片数,defaultMinPartition)】
5-3 读取HDFS文件: 【RDD分区数=max(文件的block块,defaultMinPartition)】