目录
一、Spark ON YARN模式环境搭建(主流)
1、Spark ON YARN的运行原理
在企业中,多数场景下,会将Spark运行到YARN集群中。YARN本身是一个资源调度框架,负责对运行在内部的计算框架进行资源调度管理。Spark作为典型的计算框架,其本身也可以直接运行在YARN中,并接受YARN的调度,本质上就是用Spark取代MR。对于Spark ON YARN模式,无需部署Spark集群,只需要找一台服务器,充当Spark的客户端,即可提交任务到YARN集群中运行。
在Standalone中Master负责对集群资源进行管理,Worker负责对当前机器资源进行管理。但是在Spark ON YARN模式下,资源调度任务全部由YARN取代,即Master角色由YARN的ResourceManage担任,Worker角色由YARN的NodeManage担任,Driver角色在YARN容器内或提交任务的客户端进程中(可以手动选择),Executor运行在YARN提供的容器中。
Spark ON YARN的本质就是资源管理全部由YARN担任,Spark只作为计算框架,即Spark只负责具体任务的执行,取代以前性能低的MR,Spark的Driver和Excutor都在YARN的容器中执行。ApplicationMaster是YARN自己的任务管家不起作用(但是会与Driver绑定在一起)。也就是在Spark ON YARN模式下Spark只有Driver和Excutor两个角色。
2、Spark ON YARN模式部署和测试
前提:A、YARN集群已经安装好
B、在整个YARN集群中有一台Spark客户端工具,比如可以使用spark-submit,将Spark程序提交到YARN容器中运行
在这里我们已经把YARN集群安装好了,且node11,22,33都有Spark即有3台Spark客户端工具(实际生产环境中只需要1台即可)后续的测试使用node11为Spark客户端。因此Spark ON YARN就已经配置好了,就是这么简单!!!,Spark ON YARN的配置只需要保证在spark-env.sh文件中配置好HADOOP_CONF_DIR和YARN_CONF_DIR即可,这个配置我们在上文已经配置好了。
测试:
bin/pyspark # 以local模式启动
bin/pyspark --master Standalone模式地址 # 以Standalone模式启动
bin/pyspark --master yarn # 以Spark ON YARN模式启动
打开YARN的WEB UI:node11:8088
注意与Master的WEB UI界面一样YARN的WEB UI(8088)只记录程序pysparkshell的执行情况,具体该程序下的子Job不会记录。需要打开node11:4040查看。
在node11:8088中点击Application Master也可以打开4040端口页面,这是YARN的webproxyserver提供的web跳转服务。
二、Spark ON YARN的运行模式
1、Cluster和Client的区别
Spark ON YARN有两种运行模式:Cluster模式 | Client模式(默认模式),者两种模式的唯一区别就是Driver的运行位置。
Cluster模式即Driver运行在YARN容器内部,和ApplicationMaster在同一个容器内;Client模式即Driver运行在客户端进程中,比如Driver运行在spark-submit程序的进程中。
(1)Cluster模式
(2)Client模式
可以看到Cluster模式Driver和Executor都在容器内部,因此通信过程一定要比Client模式这种垮集群的通信快,那Cluster模式效率高为什么还会存在Client模式呢?因为Cluster模式查看日志不太方便,在Cluster模式下Driver在容器内部,那程序/任务在运行过程中产生的日志也一定在YARN容器内部,在这种场景下,我们想要快速的验证一些结果就不太方便,需要进入到容器内部才能看到Driver的控制台输出,因此在某些场景下比如我们只是想简单的在YARN上测试一些东西,如果还要先到容器内部查看日志就不太方便了,Client模式的好处是Driver在客户端进程中,可以直接在终端查看日志信息。但是Client的性能肯定没有Cluster模式好。在Cluster模式下Driver被ApplicationMaster绑定,如果Driver出现问题,yarn会自动重新启动ApplicationMaster(Driver),如果是Client模式,Driver出现问题则无法使用了。
2、演示
Client模式测试:
打开node11:18080
然后打开Executors:
Cluster模式测试:
3、两种模式任务提交流程
(1)Client模式任务提交流程
A:本地机器一旦开启了一个Spark程序,会自动在该机器上开启一个Driver进程,然后Driver向ResourceManager申请启动ApplicationMaster。
B:随后ResourceManager分配Container容器,在合适的NodeManager上启动ApplicationMaster,ApplicationMaster的作用就是向ResourceManager申请Executor。
C:ApplicationMaster会向Driver询问需要什么资源,Driver会反馈需要 --num-executor 3个,--total-executor-cores 3个.....这些资源,然后ApplicationMaster会向ResourceManager申请资源。ResourceManager收到申请后,先检查资源够不够,如果资源够,会给ApplicationMaster返回资源的地址等信息。
D:ApplicationMaster收到ResourceManager的反馈后,在指定的NodeManager上分配容器启动Executor进程。Executor进程启动后会向Driver反向注册,Executor全部注册完成后Driver就能和所有的Executors通信了,开始执行任务的main函数。
E:之后执行到一个API算子时,会触发一个Job,并根据宽依赖划分Stage,每个Stage生成对应的TaskSet,之后将Task分发到各个Executor上执行。
(2)Cluster模式任务提交流程
注意:无论时Client模式还是Cluster模式,一定都是ResourceManager先在一个合适的NodeManager上分配一个容器,启动ApplicationMaster。然后ApplicationMaster得到ResourceManager的资源反馈后,再在指定的NodeManager上分配容器,启动Executor。
三、PySpark类库
1、框架 VS 类库:它两不是同一个概念
类库就是一推别人写好的代码,直接导入进行使用,例如python的pandas,numpy等等。
框架是可以独立运行,并提供编程结构的一种软件产品,例如Spark就是一个独立的框架,Hive,Hadoop都是框架。前文所安装的Spark就是Spark框架,只不过该框架提供了python的API。类库像pandas用于小规模数据集的处理,框架Spark用于大规模数据集的处理。
上文执行的bin/pyspark是一个交互式解析器程序,相当于是python的解析器,只不过其内部默认情况下有pyspark类库,默认情况下已经通过pyspark类库下的SparkContext和SparkConf构建了Spark环境sc,直接使用即可。在生产环境中一般不用pyspark解析器因为在该解析器内部只能写一行代码,通常都是在外部写好.py文件通过spark-submit提交的方式来执行,因此在外部写Spark API需要手动import pyspark。
2、PySpark类库安装:
在node11,22,33上都要安装pyspark类库
四、本机开发环境搭建
本小节将在本地计算机中安装python和pycharm,后续所有的Spark API代码开发都在本地计算机的pycharm中,然后将开发的代码提交到linux系统搭建的Spark ON YARN环境中运行。Spark的运行需要python环境,因此Linux中需要安装python相关组件,本地计算机中的python用于具体的开发任务,此外Spark 的开发可能会用到hadoop的部分功能,因此需要在本地计算机配置hadoop。
1、将hadoop-3.3.0文件中的hadoop.dll文件(hadoop的动态连接库)复制到C:\Windows\System32里面去,然后配置HADOOP_HOME环境变量指向hadoop-3.3.0文件夹位置:
2、在本地计算机上安装python,然后安装pyspark类库(如果本身电脑有python直接用即可,如果没有安装,则可以按照在linux中的安装思路下载一个Windows系统的Anaconda3来安装python)
3、安装好pycharm,新建一个项目:
4、配置ssh远程python解析器:PySpark类库支持在Windows上执行,但是毕竟PySpark与Spark相关属于大数据体系,在Windows上执行会有性能问题以及一些小bug, 在Linux上执行是完美和高效的。所以,我们也可以配置好Linux上的远程解释器, 来运行Python Spark代码。
注意配置ssh远程解析器需要pycharm专业版
配置好以后需要等一会,此时pycharm正在进行项目同步,即建本地创建的项目同步到Linux系统下。
在本地执行一下main.py文件本质上是在执行同步过去的main.py文件:
5、测试
这里的数据使用的是HDFS上的数据,当然也支持本地的数据,我们随意在本地计算机上创建一个world.txt文件,文件中输入一些单词,该文件会自动同步到Linux系统中。
如果Linux系统中没有该文件我们可以手动同步一下:
默认情况下是会自动同步的(保存文件或run),打开File Transfer后,对当前项目的任何文件进行修改,立刻会看到Automatic upload:
如果发现当前的电脑没有同步,说明没有开启自动同步,需要手动配置一下:
将数据文件直接设置为Linux系统下由Windows同步过去的文件,可以正常执行,说明确确实实本质上使用的数据就是Linux系统下的数据。
测试使用Windows自己的python解析器:在运行代码之前需要在代码中添加下面两行代码,告诉Spark本机的python在哪里,否则会报错。
import os
os.environ['PYSPARK_PYTHON'] = r"D:\bigdata\python\python.exe"
可以看到正常执行了,但是会出现一些警告,这就是因为pyspark类库毕竟是与Spark相关与Windows兼容性不高,会出现各种小bug。因此建议使用Linux中的python解析器运行。
上述添加的两行代码相当于配置一个系统变量,只不过是临时的,每次编写新的.py文件都需要添加以上两行代码,我们可以将其设置为永久:设置好以后重启pycharm就生效了,新添的那两行代码就可以去掉了。
6、将代码提交到集群环境中运行
注意上述的所有测试,spark的部署模式都是local[*],即上述代码的执行并不在我们搭建的Spark ON YARN集群环境上。首先将上述代码的setMaster部分删除,因为提交到集群可以通过客户端工具的参数指定master,比如spark-submit工具。所以,我们不在代码中固定master的设置,不然客户端工具参数无效, 代码的优先级是最高的。
此外需要注意的是,将代码提交到集群环境中时,代码里面所用的数据必须是hdfs文件系统上的数据。因为集群环境下,数据会进行切片,假设有3台服务器,每天服务器都会处理一部分数据。如果使用的是本地的数据即只有1台服务器有数据,其他两台服务器本身连数据都找不到,导致无法运行。
如果使用的是本地的python解析器,则需要手动将代码拷贝到Linux系统上,如果使用的是远程解析器,则代码会自动同步过去,直接使用即可。
执行完成后打开18080端口,就可以看到历史信息了。
此时只有node11节点有该数据,node22,33没有数据,再次执行spark-submit,指定excutor的数量为6个,确保node22,33工作。如果不指定excutor的数量由于当前的任务特别简单,可能该任务只会在1个excutor上执行,假设这个excutor在node11上,则也会正常执行。
因此在集群上运行时,数据的路径一定要写hdfs路径。具体执行过程如下:
7、分布式代码执行分析
当所有的Executors向Driver反向注册以后,就要开始执行应用程序的main函数代码了,这里以上述单词计数程序为例分析代码执行流程。
Python ON Spark执行原理:
Spark框架是由java开发出来的,最初是不能在python中运行的,但是由于python越来越火热,因此Spark官方在原有Spark的基础上进行了扩充,让其支持python,如下图所示(灰色部分就是扩充的内容):
PySpark宗旨是在不破坏Spark已有的运行时架构,在Spark架构外层包装一层Python API,借助Py4j实现Python和Java的交互,进而实现通过Python编写Spark应用程序
在Driver端具体运行的是JVM,在Executor端具体运行的是python进程
五、RDD的介绍
1、RDD的定义
弹性分布式数据集(Resilient Distributed Datasets,RDD)是Spark框架中的一个统一数据抽象对象,可以实现在内存中进行集群化运算,且是高度容错的。RDD是Spark的核心,它代表了一个不可变,可分区,里面的元素可并行计算的集合。
Datasets:一个数据集合,用于存储数据,通list,dict一样
Distributed:RDD中的数据是分布式存储的,可用于分布式计算
Resilient:RDD中的数据可以存储在内存中或者磁盘中,且分区数可以动态增加和减少
2、RDD的五大特性
(1)RDD是有分区的
RDD的分区是RDD数据存储的最小单位,一份RDD数据本质上会分成多个分区
rdd.glom()算子可以将RDD对象的分区排布显示出来。可以看到非kv型的数据分区就是均分。
(2)API算子会作用在所有分区上
(3)RDD之间是有依赖关系的
比如通过RDD1会产生RDD2,RDD2依赖与RDD1,RDD3依赖与RDD2......,会形成一个依赖链条,这个链条称为RDD的血缘关系,也称为RDD的迭代计算:textFile-->RDD1-->RDD2-->RDD3-->RDD4。
(4)k-v型的RDD可以有分区器
默认分区器:hash分区规则,可以通过rdd.partitionBy的方法手动设置分区器。该特性只针对k-v型的RDD,即RDD中存储的是二元元组:("hadoop", 2)。
(5)RDD计算过程Executor的规划,会尽量靠近数据所在的服务器
该特性是RDD中提升计算性能的特性。有了这个特性,当对RDD对象进行API计算之前,假设当前有3台服务器,RDD对象的分区分别在node11,node22上,node33上没有,即node33上没有数据。假设我们设置了Executor的数量为2,那么这两个Executor会分配在node11,22上,因为离数据本身很近,如果将Executor放到node33上,node33本身没有数据,执行计算任务需要先从别的机器上读取数据进行网络传输,然后在计算。将Executor规划到靠近数据所在的服务器,可以避免网络读取,做到本地读取,加快执行效率。Spark会在确保并行计算能力的前提下,尽量确保本地读取,这里是尽量,不是100%,因此该特性是可能的。
3、HelloWorld案例分析
六、RDD的常见API算子
1、RDD的创建
有两种方式,分别是将本地的可迭代的对象转化为RDD,以及将文件中的数据转化为RDD
方式一:本地-->RDD
方式二:文件-->RDD
对于读取文件来说,如果不指定参数2,它的默认分区数与CPU无关,主要与文件的大小有关,如果读取的数据来自hdfs则默认分区数与block块的数量有关。因此默认分区数是动态变化的。
除了textFile算子可以读取文件之外,wholeTextFile算子也可以读取文件(该算子主要适用于小文件过多的情况,该算子在读取时会自动对其进行优化),假设当前有一个文件夹tiny_files,里面存储了大量的小文件,通过wholeTextFile算子来读取文件夹tiny_files时,会自动对小文件进行优化,返回的结果是kv型数据,k是每个小文件的路径,v的小文件的值。
2、RDD的算子
在Spark中算子共分为两类:Transformation转换算子(返回值仍然是RDD对象,例如map,flatMap,reduceByKey),Action动作/行为算子(返回值不是RDD对象,例如collect)。
Transformation算子是lazy懒加载的,它会受到Action算子的驱动,如果没有Action算子,Transformation算子是不工作的。Transformation算子相当于构建执行计划,Action是一个指令让这个执行计划开始工作。
(1)map算子-T
功能:将RDD对象中的每条数据按照给定的逻辑(传入的处理函数)来处理,传入的函数支持lambda表达式。
(2)flatMap算子-T
功能:对RDD对象执行map算子,然后进行解除嵌套操作。解除嵌套就是:
(3)reduceByKey算子-T
功能:针对k-v型的RDD(二元元组),自动按照key分组,然后按照给定的函数,完成组内数据的聚合操作。
(4)mapValues算子-T
功能:针对k-v型的RDD,对其内部的value执行map操作
(5)groupBy算子-T
功能:将RDD的数据进行分组,传入的函数就是指定分组规则。
(6)filter算子-T
功能:过滤想要的数据进行保留,函数返回值为True的数据被保留,为False的数据被丢弃
(7)distinct算子-T
功能:去重
(8)union算子-T
功能:两个RDD合并为1个RDD,只合并不会去重,且不同类型的RDD也可以合并
(9)join算子-T
功能:对两个RDD实现SQL中的内、外连接,join算子只能用于kv型数据,在关联时会按照key进行关联。
(10)intersection算子-T
功能:求两个RDD的交集
(11)glom算子-T
功能:将RDD的数据加上嵌套,这个嵌套是按照分区来进行的。就是将RDD对象的分区排布显示出来。
(12)groupByKey算子-T
功能:针对kv型的RDD自动按照key分组,类似与groupBy,groupBy需要指定分组逻辑,而groupByKey不需要指定分组逻辑,自动按照key分组。reduceByKey是自动按照key分组,然后按照聚合逻辑对各组进行聚合操作。
groupByKey的返回值与groupBy一样都是kv型,k是key,v是可迭代的对象,需要进一步处理才可以具体看到内容。注意与groupBy不同的是groupBy返回的是同组数据的整体,而groupByKey返回的是同组数据的value。
(13)sortBy算子-T
功能:基于指定的排序依据对RDD数据进行排序
注意:分区数如果设置的不为1,可能产生的结果不是完全排好序的结果,因为分区数不为1,即执行排序的过程在多个分区中进行,此时只能保证在每个分区内有序,想要做到全局有序,必须将numPartitions=1。这里分区数设为3也是全局有序啊?为什么?因为spark的部署模式是local即单机模式,分区数无论设为多少,本质还是在一台服务器上运行。
(14)sortByKey算子-T
功能:针对kv型的数据,按照key进行排序
案例:
"""
从json文件orders.txt文件中找到北京所出售的商品,同时对结果集进行去重, 得到北京售卖的商品类别信息
"""
from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON'] = r"D:\bigdata\python\python.exe"
if __name__ == '__main__':
conf = SparkConf().setAppName("111").setMaster("local[*]")
sc = SparkContext(conf=conf)
rdd = sc.textFile("./orders.txt").flatMap(lambda x: x.split("|")).map(lambda x: json.loads(x)).\
map(lambda x: (x["areaName"], x["category"])).filter(lambda x: x[0] == '北京').distinct()
print(rdd.collect())
将案例提交到YARN中运行:
方式一:在pycharm中直接执行(远程解析器)
修改权限再次执行即可。打开node11:8088可以看到test-yarn-1在运行。可能会出现以下错误:
错误的原因是没有设置CapacityScheduler或者设置的scheduler不是CapacityScheduler,需要在hive-site.xml中设置以下配置:
<!-- 选择调度器,默认容量 -->
<property>
<description>The class to use as the resource scheduler.</description>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
</property>
注意在实际生产环境下开发代码时,不可能在一个.py文件中就将所有的逻辑都写完,一般都是会涉及多个.py文件,这里我们将上述代码倒数第三行的代码,脱离出来,反倒单独的一个文件中来模拟多个.py文件依赖执行的情况,并将其放到YARN集群中运行:
发现明明我把defs_19导入进来了为什么会找不到呢?这就是分布式集群的问题,如果程序是单机运行的不会出现这种错误。与使用集群环境数据必须来自与hdfs一样,运行的主代码在运行时会自动放到集群环境中,但是其余的文件并不会自动放到集群环境中,因此在集群环境下运行找不到文件。
方式二:spark-submit方式
将上述两个代码手动上传至linux系统下的任意目录下,删除主代码中的setMaster。由于在Spark环境中已经设置了HADOOP_CONF_DIR,代码中也可以删除。在执行命令时可以通过--py-files参数执行额外提交的py文件,因此代码中conf.set需要删除。
spark-submit --master yarn --py-files ./defs_19.py ./main.py