一、学习
1,安装Spark,并了解基础操作
首先安装上Spark,再执行一下基础操作,就可以了。这里的目的是通过Spark的Shell,了解一下Spark的基础操作。接下来看看文章下面的一些概念和作用什么的就可以,不用看的太细。
2,了解如何使用Java编写Spark程序
(1)先看一下官方的文档。如果对于不了解Spark的人来说,直接看官方文档可能很难理解,所以在官方文档下面有一个中文版的官方文档。
(2)在看官方文档时,会看到关于RDD中使用Closure的问题,对于这个问题可以看一下下面3个文档来了解一一下。
- 理解Spark中的闭包(closure)
- Spark——共享变量
- Spark 3. RDD 操作一 基础 ,放入方法,闭包,输出元素, 使用 K-V 工作
(3)在看官方文档时,可能还会看到一些Driver、Node、Partition等词汇。想了解关于Spark的一些基础概念的话,可以看下面的文章。
- Spark里几个重要的概念及术语:这个文章对基本概念作了简单说明。
- 『 Spark 』2. spark 基本概念解析:这个文章对基本介绍的比较多,而且这个博客的其它博文,对基础概念大部都做了很详细的介绍。可以看看其它的文章。
- Distributed Systems Architecture:这个文章里有很多关于Spark结构的图,介绍的看起来挺详细的。而文章里还有其它文章的链接,想详细知道的可以看看。
还有一个关于Spark架构的PPT和它的讲演视频: - Spark Architecture Video
- Spark Architecture PPT
(4)在看官方文档时,会看到一些Map和Reduce的API,下面的文章,让你能快速知道这个API的用法。
3,启动 spark 方法。
(1)学习文章
- 运行您的第一个 Spark 应用:装完环境后,测试 spark 环境是否可用
- Spark集群安装和使用:spark on yarn
- YARN 上运行 Spark 应用: spark on yarn 原理
(2)常用命令
1,上传 jar 到 hdfs
hdfs dfs -put spark-test-1.0-SNAPSHOT.jar hdfs://nameservice1/test/sjp/monitor/spark-test.jar
2,启动 spark
spark2-submit --class test.SparkStreamingServiceDemo5 --deploy-mode cluster --master yarn hdf//nameservice1/test/sjp/monitor/spark-test.jar cloud_aggregation cm_input cm_output 10.31.187.4:9092
二、经验
1,安装完 YARN 后,安装 Spark 是否还需要 worker?
个人感觉不用。因为原来 Work 的工作,都由 NodeManager 来处理了。
- Spark(一): 基本架构及原理
- Spark Standalone架构设计要点分析:对 standalone 模式和原理讲的挺好的。
2,停止正在运行在 yarn 上的 spark 程序。
1,使用下面的命令找出对应的 application。yarn application -list
2,杀死 application。yarn application -kill application_1537252157889_00023
参考:如何使用 Curl 停止 Spark 中某个正在运行的 Yarn 作业
3,关于 Spark 程序的打包
15,在打包时,使用 maven-shade-plugin 打包,org.apache.spark group 的依赖设置成 <scope>provided</scope>
,这些包不用打包,在环境上都有。
jar 大小可以减少很多,包含 spark-core_2.11、spark-streaming_2.11、spark-streaming-kafka-0-10_2.11 的包有 100多M,不包含只有 500K。其它的 jar 还是要包含的。
参考:利用maven-shade-plugin打包包含所有依赖jar包
为了避免每次spark上传很大的依赖包,还可以把大部分依赖放到hdfs上,然后在spark_default中指明spark.yarn.jars到hdfs目录
4,本地程序调试,不使用 setMaster
VM options中输入“-Dspark.master=local”
5,如何在Windows中使用Intellij idea搭建远程Hadoop开发环境?
https://www.zhihu.com/question/50820730/answer/124755135
6,spark 设置 hadoop 等配置方法
1,在程序内部设置(测试过)
sparkConf.set(“spark.hadoop.dfs.replication”, “1”);
2,命令行设置(没有测试过)
spark-submit --conf spark.hadoop.fs.s3a.access.key=value
3,在程序内,读 hdfs-site.xml 等配置文件(没有测试过)
基本方法就是把这些文件放到 classpath 下,方法有 2 个。
- 方法1:
把 hdfs-site.xml 文件放到 resources 目录下。 - 方法2:
Custom Hadoop Configuration for Spark from Python (PySpark)?
更多参考:Spark Configuration
7,使用 saveAsHadoopFile 时生产 _SUCCESS 文件问题
使用 saveAsHadoopFile 保存文件时,有时候会产生 _SUCCESS 文件。使用下面的设置就可以不产生这个文件:
sparkConf.set(“spark.hadoop.mapreduce.fileoutputcommitter.marksuccessfuljobs”, “false”);
参考:How to avoid _success file in Mapreduce Output Folder. 我产生 _SUCCESS 文件的原因。
8,把 JavaPairDStream 输出到多个文件
(1)如果要输出到多个文件到指定目录,并且指定文件名的话,需要自己实现输出类。这个输出类要继承 MultipleOutputFormat 类。MultipleOutputFormat 是输出到多个文件的类,但如果要输出成我们指定的文件名的话,需要自己实现一些方法。
(2)在输出时,可以使用两个方式:
- Dstream.saveAsHadoopFiles:这种方式加上面的 MultipleOutputFormat 实现类,可以完成我们的需要。但必须还要输出以 “prefix-TIME_IN_MS[.suffix]” 为名字的文件夹,而且是必须输出。
- JavaRDD.saveAsHadoopFiles:可以不必须输出临时文件夹。但要输出一个 _SUCCESS 文件。设置
sparkConf.set("spark.hadoop.mapreduce.fileoutputcommitter.marksuccessfuljobs", "false")
可以避免输出这个文件。
(3)在输出时,如果要输出到同一个文件中的话,可使用 rdd.coalesce(1) 把数据重新分区。如果 rdd 已经是 reduceByKey 这种已经分区完成的 rdd 的话,并且要按 key 相同的输出到一个文件的话,应该可以不使用 rdd.coalesce(1) 。要注意:如果不同的分区的数据,要交差输出到不同的文件的话,可能会有问题。
coalesce 功能是重新分区,相当于 repartition(x, true)。
pair.foreachRDD(rdd->{
if (rdd.count() != 0) {
rdd.saveAsHadoopFile("hdfs://localhost:9000/test/", String.class, String.class, RDDMultipleAppendTextOutputFormat.class);
// rdd.coalesce(1).saveAsHadoopFile("hdfs://localhost:9000/test/", String.class, String.class, RDDMultipleAppendTextOutputFormat.class);
}
});
参考:
- how to make saveAsTextFile NOT split output into multiple file?:讲了为什么要使用 rdd.coalesce(1)
- spark-streaming多目录追加写:参考这个写的 writer 代码。
- spark streaming 实现根据文件内容自定义文件名,并实现文件内容追加:这个是 java 版本的一个实现。修改的类太多。不像上面那个符合设计要求。
9,Idea 开发时常配置的参数
在进行 Idea 开发时,需要配置一些本地开发用的参数。如果这些参数用代码写到程序中,在到生产环境使用时,忘记需要注释掉会很麻烦。所以,把一些参数在 Idea 的Run/Debug Configurations
中进行设置,不会影响代码。常用配置如下:
(1)在 VM Option:
- -Dspark.master=local :设置本地启动
- -Dspark.hadoop.dfs.replication=1 :设置 hadoop 写的数据的副本为 1 份。一般在自己机器上安装 hadoop 时,都是单 dataNode 的。如果不设置这个,Append 数据时可能会产生一个错误。
- -Dspark.hadoop.fs.default.name=hdfs://localhost:9000 :设置 Hadoop 的 默认服务名。一般在自己机器上安装 hadoop 时,如果在这里设置些项目的话,使用
hdfs:///xxx
方式的话,会报错。如果写命名hdfs://nameserver/xxx
的话不会报错,但这样程序的可移植性就低了。
10,注意进行 cache
请看下面的代码,下面的代码逻辑很简单:1,生成 pair 2,保存 pair。如果像下面这样做的话,生成 pair
代码里面的System.out.println("pair called");
会输出 4 遍。但如果在生成 pair
和保存pair
代码之间加 pair.cache
的话,就会只输出 2 遍。这说明,不进行 cache 的话,保存时使用 pair 时候,又进行了一次和生成 pair
一样的过程。所以要注意使用 cache 功能。
// 生成 pair
JavaPairDStream<String, String> pair = messages.map(ConsumerRecord::value)
.mapToPair(log -> {
System.out.println("pair called");
return new Tuple2<>("", "");
}
});
// 保存 pair
pair.foreachRDD(rdd->{
long rddCnt = rdd.count();
if (rddCnt != 0) {
rdd.saveAsHadoopFile("", String.class, String.class, CustomClass.class);
}
});
可能又会想,如果把生成
和保存
操作,像下面这样连上写,会不会只生成 2 遍呢?经过测试,也会生成 4 遍。所以,要注意进行 cache。
// 生成并保存 pair
messages.map(ConsumerRecord::value)
.mapToPair(log -> {
System.out.println("pair called");
return new Tuple2<>("", "");
}
}).foreachRDD(rdd->{
long rddCnt = rdd.count();
if (rddCnt != 0) {
rdd.saveAsHadoopFile("", String.class, String.class, CustomClass.class);
}
});
11,在 transform 中 print
spark 的设计是在 transform 时,我们想打印 transform 过程中的值。在本地测试时是可以看到的,但在 yarn cluster 模式下,看不到 print 的内容。例如:
JavaPairDStream<String, String> servicePair = messages.map(ConsumerRecord::value)
.filter(log -> log.contains("\"logType\":\"service\""))
.mapToPair(log -> {
try {
// 下面的输出,不会输出到 driver 的 stdout log 中,会输出到 Exectutor Log 中。
System.out.println("service log:" + log);
String key = ...省略...;
return new Tuple2<>(key, ...省略...);
} catch (Exception e) {
e.printStackTrace();
return new Tuple2<>("", "");
}
});
原因应该是,yarn cluster 收集的 stdout log 是 driver 的输出,不是 Executor 的输出。transform 中的 print 输出,输出到了 Exectutor log 中。想看 Executor log 的话,可以从 spark 的 WebUI 中看到。进入 WebUI 后,选择上面菜单栏中的Executors
,然后可以看到 Executors 列表。在列表中,最后一列是 log 列,可以选择看 stdout 或 stderr log。
这篇文章 Spark losing println() on stdout 是 print 看不到相关的一篇文章,点赞最多的回答可能不是正确的,但每个回答下面的评论非常好,可以看看。
12,关于 Spark Streaming 中 LocationStrategies 的设置
- LocationStrategies.PreferBrokers():仅仅在你 spark 的 executor 在相同的节点上,优先分配到存在 kafka broker 的机器上;
- LocationStrategies.PreferConsistent():大多数情况下使用,一致性的方式分配分区所有 executor 上。(主要是为了分布均匀)
- LocationStrategies.PreferFixed():如果你的负载不均衡,可以通过这两种方式来手动指定分配方式,其他没有在 map 中指定的,均采用 preferConsistent() 的方式分配;
13,什么样的类需要声明 接口?
- 在 Driver 里声明的
类实例
,在 Excecutor 里使用时,这个类
需要声明 Serializable 接口。 - 不需要实例化的
工具类
,不需要声明 Serializable 接口。 - 每个实例都是在 Executor 上合建的类,不需要声明 Serializable 接口。
参考:
- Spark 中的序列化陷阱
- Spark中的序列化机制
14,流式统计的几个难点
流式统计的几个难点
15,Spark Streaming 数据进行排序
使用 transform 或 transformToPair 进行排序。transform 可以把 rdd 转化成我们想要的类型,所以 transform 方法的 Function 需要返回一个 rdd。注意:这个 Function 是 spark 类包中的接口。
JavaPairDStream<Integer,String> sortedStream = swappedPair.transformToPair(
new Function<JavaPairRDD<Integer,String>, JavaPairRDD<Integer,String>>() {
@Override
public JavaPairRDD<Integer,String> call(JavaPairRDD<Integer,String> jPairRDD) throws Exception {
return jPairRDD.sortByKey(false);
}
});
Spark
一、Spark 与 hadoop
Hadoop有两个核心模块,分布式存储模块HDFS和分布式计算模块Mapreduce。spark本身并没有提供分布式文件系统,因此spark的分析大多依赖于Hadoop的分布式文件系统HDFS。Hadoop的Mapreduce与spark都可以进行数据计算,而相比于Mapreduce,spark的速度更快并且提供的功能更加丰富
二、Spark 名词
- Application:Spark中的Application和Hadoop MapReduce中的概念是相似的,指的是用户编写的Spark应用程序,内含了一个Driver功能的代码和分布在集群中多个节点上运行的Executor代码
- Driver Program:Spark中的Driver即运行上述Application的main()函数并且创建SparkContext,其中创建SparkContext的目的是为了准备Spark应用程序的运行环境。在Spark中由SparkContext负责和ClusterManager通信,进行资源的申请、任务的分配和监控等;当Executor部分运行完毕后,Driver负责将SparkContext关闭。通常用SparkContext代表Driver
- Executor:Application运行在Worker节点上的一个进程,该进程负责运行Task,并且负责将数据存在内存或者磁盘上,每个Application都有各自独立的一批Executor。在Spark
on Yarn模式下,其进程名称为CoarseGrainedExecutorBackend,类似于Hadoop MapReduce中的YarnChild。一个CoarseGrainedExecutorBackend进程有且仅有一个executor对象,它负责将Task包装成taskRunner,并从线程池中抽取出一个空闲线程运行Task。每个CoarseGrainedExecutorBackend 能并行运行Task的数量就取决于分配给它的CPU的个数了 - Cluster Mananger:指的是在集群上获取资源的外部服务,目前有:
- Standalone:Spark原生的资源管理,由Master负责资源的分配;
- Hadoop Yarn:由YARN中的ResourceManager负责资源的分配;
- Worker:集群中任何可以运行Application代码的节点,类似于YARN中的NodeManager节点。在Standalone模式中指的就是通过Slave文件配置的Worker节点,在Spark
on Yarn模式中指的就是NodeManager节点 - Job:包含多个Task组成的并行计算,往往由Spark Action催生,一个JOB包含多个RDD及作用于相应RDD上的各种Operation
- Stage:每个Job会被拆分很多组Task,每组任务被称为Stage,也可称TaskSet,一个作业分为多个阶段
- Task:被送到某个Executor上的工作任务
三、Spark 架构
spark 架构都是 Driver 通过 Cluster Manager 申请资源(Executor),然后 Driver 直接和 Executor 进行通信,Driver 分配 Task 给 Exectuor 去执行。
例如,下图就是前面说明的流程。SparkContext 就是 Driver,Driver 先去资源管理器(也就是 Cluster Manager)申请资源。Cluster Manager 分配它所管理的资源。然后资源直接和 Driver 进行通信,向 Driver 注册并申请 Task 进行执行。
Cluster Manager 的主要作用就是分配资源,分配完资源后,资源就直接和 Driver 进行工作。这种架构的好处是,Cluster Manager 是可以替换的,可以使用 Spark 自带的 Cluster Manager、Yarn、Mesos 等 Cluster Manager。下图就是架构的概括图,根据不同的 Cluster,各个部分的名称也不一样,稍后介绍每个 Cluster 。
这种架构的特点是:
- 每个Application获取专属的executor进程,该进程在Application期间一直驻留,并以多线程方式运行Task。这种Application隔离机制是有优势的,无论是从调度角度看(每个Driver调度他自己的任务),还是从运行角度看(来自不同Application的Task运行在不同JVM中),当然这样意味着Spark Application不能跨应用程序共享数据,除非将数据写入外部存储系统。
- Spark与资源管理器无关,只要能够获取executor进程,并能保持相互通信就可以了
提交SparkContext的Client应该靠近Worker节点(运行Executor的节点),最好是在同一个Rack里,因为Spark Application运行过程中SparkContext和Executor之间有大量的信息交换。 - Task采用了数据本地性和推测执行的优化机制
1,Spark standalone
Standalone模式使用Spark自带的资源调度框架,采用Master/Slaves的典型架构,选用ZooKeeper来实现Master的HA。框架结构图如下:
独立集群管理器支持两种部署模式。在这两种模式中,应用的驱动器程序运行在不 同的地方。
- 在客户端模式中(默认情况),驱动器程序会运行在你执行 spark-submit 的机 器上,是 spark-submit 命令的一部分。这意味着你可以直接看到驱动器程序的输出,也 可以直接输入数据进去(通过交互式 shell),但是这要求你提交应用的机器与工作节点间有很快的网络速度,并且在程序运行的过程中始终可用。
- 在集群模式下,驱动器程序会作为某个工作节点上一个独立的进程运行在独立集群管理器内部。它也会连接主节点来申请执行器节点。在这种模式下,spark-submit 是“一劳永逸”型,你可以在应用运行时关掉你的电脑。
你还可以通过集群管理器的网页用户界面访问应用的日志。向 spark- submit 传递 --deploy-mode cluster 参数可以切换到集群模式。
这种架构的具体的执行流程如下:
- SparkContext连接到Master,向Master注册并申请资源(CPU Core 和Memory)
- Master根据SparkContext的资源申请要求和Worker心跳周期内报告的信息决定在哪个Worker上分配资源,然后在该Worker上获取资源,然后启动StandaloneExecutorBackend;
- StandaloneExecutorBackend向SparkContext注册;
- SparkContext将Applicaiton代码发送给StandaloneExecutorBackend;并且SparkContext解析Applicaiton代码,构建DAG图,并提交给DAG Scheduler分解成Stage(当碰到Action操作时,就会催生Job;每个Job中含有1个或多个Stage,Stage一般在获取外部数据和shuffle之前产生),然后以Stage(或者称为TaskSet)提交给Task Scheduler,Task Scheduler负责将Task分配到相应的Worker,最后提交给StandaloneExecutorBackend执行;
- StandaloneExecutorBackend会建立Executor线程池,开始执行Task,并向SparkContext报告,直至Task完成
- 所有Task完成后,SparkContext向Master注销,释放资源
Spark on Yarn
Spark on YARN模式根据Driver在集群中的位置分为两种模式:一种是YARN-Client模式,另一种是YARN-Cluster(或称为YARN-Standalone模式)。Yarn-Client模式中,Driver在客户端本地运行,这种模式可以使得Spark Application和客户端进行交互,因为Driver在客户端,所以可以通过webUI访问Driver的状态,默认是http://hadoop1:4040访问,而YARN通过http:// hadoop1:8088访问。
YARN-client
- Spark Yarn Client向YARN的ResourceManager申请启动Application Master。同时在SparkContent初始化中将创建DAGScheduler和TASKScheduler等,由于我们选择的是Yarn-Client模式,程序会选择YarnClientClusterScheduler和YarnClientSchedulerBackend
- ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster,与YARN-Cluster区别的是在该ApplicationMaster不运行SparkContext,只与SparkContext进行联系进行资源的分派
- Client中的SparkContext初始化完毕后,与ApplicationMaster建立通讯,向ResourceManager注册,根据任务信息向ResourceManager申请资源(Container)
一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向Client中的SparkContext注册并申请Task - client中的SparkContext分配Task给CoarseGrainedExecutorBackend执行,CoarseGrainedExecutorBackend运行Task并向Driver汇报运行的状态和进度,以让Client随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务
- 应用程序运行完成后,Client的SparkContext向ResourceManager申请注销并关闭自己
YARN-Cluster
在YARN-Cluster模式中,当用户向YARN中提交一个应用程序后,YARN将分两个阶段运行该应用程序:
- 第一个阶段是把Spark的Driver作为一个ApplicationMaster在YARN集群中先启动;
- 第二个阶段是由ApplicationMaster创建应用程序,然后为它向ResourceManager申请资源,并启动Executor来运行Task,同时监控它的整个运行过程,直到运行完成
- Spark Yarn Client向YARN中提交应用程序,包括ApplicationMaster程序、启动ApplicationMaster的命令、需要在Executor中运行的程序等。
- ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster,其中ApplicationMaster进行SparkContext等的初始化。
- ApplicationMaster向ResourceManager注册,这样用户可以直接通过ResourceManage查看应用程序的运行状态,然后它将采用轮询的方式通过RPC协议为各个任务申请资源,并监控它们的运行状态直到运行结束。
- 一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向ApplicationMaster中的SparkContext注册并申请Task。这一点和Standalone模式一样,只不过SparkContext在Spark Application中初始化时,使用CoarseGrainedSchedulerBackend配合YarnClusterScheduler进行任务的调度,其中YarnClusterScheduler只是对TaskSchedulerImpl的一个简单包装,增加了对Executor的等待逻辑等
- ApplicationMaster中的SparkContext分配Task给CoarseGrainedExecutorBackend执行,CoarseGrainedExecutorBackend运行Task并向ApplicationMaster汇报运行的状态和进度,以让ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务。
- 应用程序运行完成后,ApplicationMaster向ResourceManager申请注销并关闭自己。
Yarn Client 和 Yarn Cluster 区别
理解YARN-Client和YARN-Cluster深层次的区别之前先清楚一个概念:Application Master。在YARN中,每个Application实例都有一个ApplicationMaster进程,它是Application启动的第一个容器。它负责和ResourceManager打交道并请求资源,获取资源之后告诉NodeManager为其启动Container。从深层次的含义讲YARN-Cluster和YARN-Client模式的区别其实就是ApplicationMaster进程的区别。
- YARN-Cluster模式下,Driver运行在AM(Application Master)中,它负责向YARN申请资源,并监督作业的运行状况。当用户提交了作业之后,就可以关掉Client,作业会继续在YARN上运行,因而YARN-Cluster模式不适合运行交互类型的作业。
- YARN-Client模式下,Application Master仅仅向YARN请求Executor,Client会和请求的Container通信来调度他们工作,也就是说Client不能离开。
Spark Standalone 和 Spark on Yarn 区别
相同点:
- Client 模式下,Driver 在启动命令的本机上。
- Cluster 模式下,Driver 是在 Cluster Manager 管理的结点上(Spark 的 Worker 或 Yarn 的 Node Manager)。
不同点:
- Cluster Manager 和 资源结点:
- Spark Standalone 架构上,Cluster Manager 叫做
Master
,资源结点叫做Worker
。 - Spark on Yarn 架构上,Cluster Manager 叫做 Resource Manager,资源结点叫做
NodeManager
。
参考:
Spark(一): 基本架构及原理:这个最全,把 spark 架构、standalone、spark on yarn 架构结构都说了。
Spark的运行架构分析(一)之架构概述:第一篇文章拿出来一部分,加上自己的理解和讲解。
深入理解spark之架构与原理:第一篇文章拿出来一部分,加上自己的理解和讲解。