目录
一、spark简介
spark是什么
spark的特征
二、Spark RDD
RDD基本概念
RDD五大属性
HDFS与Partition
RDD流程图
Lineage血统
三、Spark 算子
转换算子
行动算子
控制算子
cache
persist
checkpoint
执行原理
四、任务提交方式
Standalone-client
Standalone-cluster
yarn-client
yarn-cluster
五、窄依赖和宽依赖
六、Stage
stage切割规则
stage计算模式
七、SparkShuffle
SparkShuffle概念
HashShuffle
普通机制
合并机制
SortShuffle
普通机制
bypass机制
八、Spark资源调度和任务调度
调度流程
流程图解
粗细粒度资源申请
粗粒度资源申请(Spark)
细粒度资源申请(MR)
九、广播变量和累加器
广播变量
累加器
十、Spark SQL
DataFrame
DataSet
DSL操作
action
查询
Limit
排序
组函数
去重
聚合
Union
Join
save
SparkSQL的数据源
SparkSQL底层架构
谓词下推
十一、Spark Streaming
概述
示例
DStream的输出操作
Spark Streamin运行架构
参考资料
Apache Spark是专门为大数据处理而设计的通用的计算引擎。spark拥有MapReduce所具有的优点,但不同于Map Reduce的是Job中间输出结果可以缓存到内存中,从而不再需要读写HDFS,减少磁盘数据交互,因此Spark能更好的适应机器学习和数据挖掘等需要迭代的算法。
Spark提供了Spark RDD 、 Spark SQL 、 Spark Streaming 、 Spark MLlib 、 Spark GraphX等技术组件,可以一站式地完成大数据领域的离线批处理、交互式查询、流式计算、机器学习、图计算等常见的任务。这就是 spark 一站式开发的特点。
注意
RDD的最重要的特性之一就是血缘关系(Lineage ),它描述了一个 RDD 是如何从父 RDD 计算得来的。如果某个 RDD 丢失了,则可以根据血缘关系,从父 RDD 计算得来。
Spark 记录了 RDD 之间的生成和依赖关系。但是只有当 F 进行行动操作时,Spark 才会根据 RDD的依赖关系生成 DAG,并从起点开始
package com.libing.spark.operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 转换算子是对rdd相互转换
* filter/map/flatMap/sample/reduceByKey
*
* @author liar
* @date 2022/9/4 10:31
* @version 1.0
*/
object TransformationsFun {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local").setAppName("transformation_operator")
val sc: SparkContext = new SparkContext(conf)
//数据(rdd)的产生,rdd之间的相互转换
val file: String = TransformationsFun.getClass.getClassLoader.getResource("wc.txt").getFile
val lineRDD: RDD[String] = sc.textFile(file)
// println(sc.defaultMinPartitions)
// println(sc.defaultParallelism)
//
// /**
// * filter
// * filter不会改变数据的整体结构
// */
// val resultRDD: RDD[String] = lineRDD.filter(x => {
// if (x.contains("shanghai"))
// true
// else
// false
// })
// resultRDD.foreach(println)
// println("------------------------")
//
//
// /**
// * map & flatMap
// */
// val value: RDD[Array[String]] = lineRDD.map(x => x.split(" "))
// val value1: RDD[String] = lineRDD.flatMap(_.split(" "))
// value.foreach(println)
// value1.foreach(println)
// println("------------------------")
//
//
// /**
// * reduceByKey
// */
// lineRDD.map(x => (x, 1)).reduceByKey(_ + _).foreach(println)
/**
* sortBYKey & sortBy
* 排序,sortBy指定key再去排序
* 默认升序,false降序
*/
val result: RDD[(String, Int)] = lineRDD.flatMap(x => x.split(" ")).map(x => (x, 1)).reduceByKey((x, y) => x + y)
result.sortBy(_._2).foreach(println)
result.sortBy(_._2, false).foreach(println)
result.sortBy(_._1, false).foreach(println)
result.sortByKey().foreach(println)
/**
* sample(抽样)
*/
lineRDD.sample(true, 0.5).foreach(println)
sc.stop()
}
}
wc.txt文件如下
Action 类算子叫做行动算子, Action 类算子是触发执行。
一个application 应用程序中有几个 Action 类算子执行,就有几个 job 运行。
常见Action 类算子
count :返回数据集中的元素数。会在结果计算完成后回收到 Driver 端。
take(n) :返回一个包含数据集前 n 个元素的集合。
first:效果等同于 take(1) ,返回数据集中的第一个元素。
foreach:循环遍历数据集中的每个元素,运行相应的逻辑。
collect:将计算结果回收到 Driver 端。
package com.libing.spark.operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author liar
* @date 2022/9/4 12:48
* @version 1.0
*/
object ActionFun {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local").setAppName("action_operator")
val sc: SparkContext = new SparkContext(conf)
val file: String = TransformationsFun.getClass.getClassLoader.getResource("wc.txt").getFile
val lineRDD: RDD[String] = sc.textFile(file)
/**
* 行动算子
* count foreach collect take first
*/
println("------行动算子开始-------")
println("======foreach开始=======")
lineRDD.foreach(println)
println("======foreach结束=======")
println("======count开始=======")
//计算数据集中存在的元素数量
println(lineRDD.count())
println("======count结束=======")
println("======collect开始=======")
lineRDD.collect().foreach(println)
//1.collect的作用
//Spark内有collect方法,是Action操作里边的一个算子,这个方法可以将RDD类型的数据转化为数组,同时会从远程集群里拉取数据到driver端。
//2.已知的弊端
//首先,collect是Action里边的,根据RDD的惰性机制,真正的计算发生在RDD的Action操作。那么,一次collect就会导致一次Shuffle,而一次Shuffle调度一次stage,然而一次stage包含很多个已分解的任务碎片Task。这么一来,会导致程序运行时间大大增加,属于比较耗时的操作,即使是在local模式下也同样耗时。
//其次,从环境上来讲,本机local模式下运行并无太大区别,可若放在分布式环境下运行,一次collect操作会将分布式各个节点上的数据汇聚到一个driver节点上,而这么一来,后续所执行的运算和操作就会脱离这个分布式环境而相当于单机环境下运行,这也与Spark的分布式理念不合。
//最后,将大量数据汇集到一个driver节点上,并且像这样val arr = data.collect(),将数据用数组存放,占用了jvm堆内存,可想而知,是有多么轻松就会内存溢出。
//3.如何规避
//若需要遍历RDD中元素,大可不必使用collect,可以使用foreach语句;
//若需要打印RDD中元素,可用take语句,返回数据集前n个元素,data.take(1000).foreach(println),这点官方文档里有说明;
//若需要查看其中内容,可用saveAsTextFile方法。
//总之,单机环境下使用collect问题并不大,但分布式环境下尽量规避,如有其他需要,手动编写代码实现相应功能就好。
//4.补充:
//collectPartitions:同样属于Action的一种操作,同样也会将数据汇集到Driver节点上,与collect区别并不是很大,唯一的区别是:collectPartitions产生数据类型不同于collect,collect是将所有RDD汇集到一个数组里,而collectPartitions是将各个分区内所有元素存储到一个数组里,再将这些数组汇集到driver端产生一个数组;collect产生一维数组,而collectPartitions产生二维数组。
println("======collect结束=======")
println("======take开始=======")
//返回现有数据集的前n个元素
lineRDD.take(2).foreach(println)
println("======take结束=======")
println("======first开始=======")
//在Spark中,First函数始终返回数据集的第一个元素。它类似于take(1)。
val str: String = lineRDD.first()
println(str)
println("======first结束=======")
println("------行动算子结束-------")
sc.stop()
}
}
运行结果如下:
------行动算子开始-------
======foreach开始=======
hello nihao
hello shanghai
hello beijing
hello cq
hello nihao
hello shanghai
hello nihao
======foreach结束=======
======count开始=======
7
======count结束=======
======collect开始=======
hello nihao
hello shanghai
hello beijing
hello cq
hello nihao
hello shanghai
hello nihao
======collect结束=======
======take开始=======
hello nihao
hello shanghai
======take结束=======
======first开始=======
hello nihao
======first结束=======
------行动算子结束-------
将RDD 持久化,持久化的单位是 partition
控制算子有三种: cache , persist , checkpoint 。 cache 和 persist 都是懒执行的。必须有一个 action 类算子触发执行
checkpoint 算子不仅能将 RDD 持久化到磁盘,还能切断 RDD 之间的依赖关系
package com.libing.spark.operator
import org.apache.spark.rdd.RDD
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}
/**
* cache,persist,checkpoint
* @author liar
* @date 2022/9/4 20:33
* @version 1.0
*/
object ControlFun {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local").setAppName("control_operator")
val sc: SparkContext = new SparkContext(conf)
val file: String = TransformationsFun.getClass.getClassLoader.getResource("test.txt").getFile
val lineRDD: RDD[String] = sc.textFile(file)
/**
* 控制算子(持久化算子)
*/
println("------控制算子开始-------")
/**
* cache 对rdd进行持久化到内存的操作(中间结果的持久化),能够提升性能
*/
// println("======cache开始=======")
// println("有cache的情况")
// //cache() = persist() 只放在内存
// lineRDD.cache()
// val startTime: Long = System.currentTimeMillis()
// val count = lineRDD.count()
// val endTime: Long = System.currentTimeMillis()
// println(s"此rdd共计$count 条数据,初始化数据以及cache用时为:${endTime - startTime}")
// println("----------------------")
// println("无cache的情况")
// val startTime2: Long = System.currentTimeMillis()
// val count2 = lineRDD.count()
// val endTime2: Long = System.currentTimeMillis()
// println(s"此rdd共计$count2 条数据,初始化数据以及cache用时为:${endTime2 - startTime2}")
// println("======cache结束=======")
// println("======persist开始=======")
// //DISK_ONLY会将读取的数据暂时存放进磁盘(AppData\Local\Temp\)目录下,待app运行结束将会删除临时文件
// //删除文件的日志
// //22/09/04 21:32:58 INFO ShutdownHookManager: Deleting directory C:\Users\liar\AppData\Local\Temp\spark-bf14f82c-076a-429d-b958-1b373c8d7ed8
// lineRDD.persist(StorageLevel.DISK_ONLY)
// val startTime: Long = System.currentTimeMillis()
// val count = lineRDD.count()
// val endTime: Long = System.currentTimeMillis()
// println(s"此rdd共计$count 条数据,初始化数据以及cache用时为:${endTime - startTime}")
// println("----------------------")
// println("无persist的情况")
// val startTime2: Long = System.currentTimeMillis()
// val count2 = lineRDD.count()
// val endTime2: Long = System.currentTimeMillis()
// println(s"此rdd共计$count2 条数据,初始化数据以及cache用时为:${endTime2 - startTime2}")
// println("======persist结束=======")
/**
*将这个RDD标记为检查点。它将被保存到检查点目录内的一个文件,使用SparkContext#setCheckpointDir设置,所有对其父rdd的引用将被删除。该函数必须在该RDD上执行任何作业之前调用。强烈建议将该RDD持久化到内存中,否则将其保存到文件中将需要重新计算。
*
* 执行原理:
* 1.当rdd的job执行完毕后,会从finalrdd从后往前回溯
* 2.当回溯到某一个rdd调用了checkpoint方法,会对当前的rdd做一个标记
* 3.spark框架会自动启动一个新的job,重新计算这个rdd的数据,将数据持久化到hdfs或本地
*/
println("======checkpoint开始=======")
//checkpoint 使用需要设置数据存储路径
sc.setCheckpointDir("./checkpoint")//本地
//sc.setCheckpointDir("hdfs://node01:8020/checkpoint")//hdfs(node01为主节点)
lineRDD.persist(StorageLevel.DISK_ONLY)
lineRDD.checkpoint()
val startTime: Long = System.currentTimeMillis()
val count = lineRDD.count()
val endTime: Long = System.currentTimeMillis()
println(s"此rdd共计$count 条数据,初始化数据以及cache用时为:${endTime - startTime}")
println("----------------------")
println("无persist的情况")
val startTime2: Long = System.currentTimeMillis()
val count2 = lineRDD.count()
val endTime2: Long = System.currentTimeMillis()
println(s"此rdd共计$count2 条数据,初始化数据以及cache用时为:${endTime2 - startTime2}")
println("======checkpoint结束=======")
println("------控制算子结束-------")
sc.stop()
}
}
执行流程
总结
执行流程
总结
执行流程
总结
执行流程
总结
RDD之间有一系列的依赖关系,依赖关系又分为窄依赖和宽依赖。
Spark任务会根据 RDD 之间的依赖关系,形成一个 DAG 有向无环图, DAG 会提交给 DAGScheduler , DAGScheduler 会把 DAG 划分成相互依赖的多个 stage ,划分 stage 的依据就是 RDD 之间的宽窄依赖。遇到宽依赖就划分 stage ,每个 stage 包含一个或多个 task 任务。然后将这些 task 以 taskSet 的形式提交给 TaskScheduler 运行。
stage 是由一组并行的 task 组成。
切割规则:从后往前,遇到宽依赖就切割 stage 。
总结:由于spark中stage的划分是根据shuffle来划分的,而宽依赖必然有shuffle过程,因此可以说spark是根据宽窄依赖来划分stage的。
执行流程:
总结:
执行流程:
总结:
执行流程:
bypass 运行机制的触发条件如下
shuffle reduce task 的数量小于 spark.shuffle.sort.bypassMergeThreshold 的参数 值。这个值默认是 200 。
不需要进行 map 端的预聚合,比如 groupBykey , join 。产生的磁盘小文件为: 2*M(map task的个数) 。
启动集群后, Worker 节点会向 Master 节点汇报资源情况, Master 掌握了集群资源情况。 当 Spark 提交一个 Application 后,根据 RDD 之间的依赖关系将 Application 形成一个 DAG 有向 无环图。 任务提交后, Spark 会在 Driver 端创建两个对象: DAGScheduler 和 TaskScheduler ,DAGScheduler 是任务调度的高层调度器,是一个对象。DAGScheduler 的主要作用就是将 DAG 根据 RDD 之间的宽窄依赖关系划分为一个个的 Stage ,然后将这些 Stage 以 TaskSet 的形式提交给 TaskScheduler ( TaskScheduler 是任务调度的低层 调度器,这里 TaskSet 其实就是一个集合,里面封装的就是一个个的 task 任务,也就是 stage 中 的并行的 task 任务)。TaskSchedule 会遍历 TaskSet 集合,拿到每个 task 后会将 task 发送到 Executor 中去执行(其 实就是发送到 Executor 中的线程池 ThreadPool 去执行)。task 在 Executor 线程池中的运行情况会向 TaskScheduler 反馈,当 task 执行失败时,则由 TaskScheduler 负责重试,将 task 重新发送给 Executor 去执行,默认重试3次。如果重试3次依 然失败,那么这个 task 所在的 stage 就失败了。stage 失败了则由 DAGScheduler 来负责重试,重新发送 TaskSet 到 TaskScheduler , Stage 默认重试4次。如果重试4次以后依然失败,那么这个 job 就失败了。 job 失败了, Application 就 失败了。TaskScheduler 不仅能重试失败的 task ,还会重试 straggling (落后,缓慢) task ( 也就是执 行速度比其他task慢太多的task )。如果有运行缓慢的 task 那么 TaskScheduler 会启动一个新的task 来与这个运行缓慢的 task 执行相同的处理逻辑。两个 task 哪个先执行完,就以哪个 task 的执行结果为准。这就是 Spark 的推测执行机制。在 Spark 中推测执行默认是关闭的。推测执行 可以通过 spark.speculation 属性来配置。
广播变量使用
val conf = new sparkConf(
conf.setMaster("local").setAppName("brocast")
val sc = new SparkContext(conf)
val list = List("he1lo xxx")
val broadCast = sc.broadcast(list)
val lineRDD = sc.textFile(" ./words.txt")
lineRDD.filter { x => broadcast.value.contains(x) }.foreach {println}
sc.stop()
注意事项
累加器的使用
val conf = new sparkConf()
conf.setMaster("local" ).setAppName ("accumulator")
val sc = new sparkContext(conf)
val accumulator = sc.longAccumulator
sc.textFile("./words.txt").foreach { x =>{accumulator.add(1)}print1n(accumu1ator. value)
sc.stop()
注意事项
save可以将data数据保存到指定的区域
dataFrame.write.format("json").mode(SaveMode.Overwrite).save()
SparkSQL 的数据源可以是 JSON 类型的字符串, JDBC , Parquet , Hive , HDFS 等。
首先拿到 sql 后解析一批未被解决的逻辑计划,再经过分析得到分析后的逻辑计划,再经过一批优化规则转换成一批最佳优化的逻辑计划,再经过 SparkPlanner 的策略转化成一批物理计划,随后经过消费模型转换成一个个的 Spark 任务执行。
Predicate Pushdown简称谓词下推,简而言之,就是在不影响结果的情况下,尽量将过滤条件提量,节约了集群的资源,也提升了任务的性能。前执行。谓词下推后,过滤条件在map端执行,减少了map端的输出,降低了数据在集群上传输的量,节约了集群的资源,也提升了任务的性能。
下面以一个简单的例子开始spark streaming的学习之旅!我们会从本机的7777端口源源不断地收到以换行符分隔的文本数据流
// 在本地启动名为SimpleDemo的SparkStreaming应用
// 该应用拥有两个线程,其批处理时间间隔为1s
// 创建SparkConf
val conf = new SparkConf().setMaster("local[2]").setAppName("SimpleDemo")
// 从SparkConf创建StreamingContext并指定1秒钟的批处理大小
val ssc = new StreamingContext(conf, Seconds(1))
// 创建ReceiverInputDStream,该InputDStream的Receiver监听本地机器的7777端口
val lines = ssc.socketTextStream("localhost", 7777) // 类型是ReceiverInputDStream
// 从DStream中筛选出包含字符串"error"的行,构造出了
// lines -> errorLines -> .print()这样一个DStreamGraph
val errorLines = lines.filter(_.contains("error"))
// 打印出含有"error"的行
errorLines.print()
让我们从创建StreamingContext 开始,它是流计算功能的主要入口。StreamingContext 会在底层创建出SparkContext,用来处理数据。其构造函数还接收用来指定多长时间处理一次新数据的批次间隔(batch interval)作为输入,这里我们把它设为1 秒。
接着,调用socketTextStream() 来创建出基于本地7777端口上收到的文本数据的DStream。然后把DStream 通过filter() 进行转化,只得到包含“error”的行。最后,使用输出操作print() 把一些筛选出来的行打印出来。
到此时只是设定好了要进行的计算步骤,系统收到数据时计算就会开始。要开始接收数据,必须显式调用StreamingContext 的start() 方法。这样,Spark Streaming 就会开始把Spark 作业不断交给下面的SparkContext 去调度执行。执行会在另一个线程中进行,所以需要调用awaitTermination 来等待流计算完成,来防止应用退出。
// 启动流计算环境StreamingContext并等待它"完成"
ssc.start()
// 等待作业完成
ssc.awaitTermination()
下面结合代码逐一分析SparkStreaming应用执行的过程
在Driver端中,StreamingContext初始化时会创建一些内部的关键组件如DStreamGraph、ReceiverTracker、JobGenerator和JobScheduler等。实际上,调用StreamingContext.start()方法的时候,就会在Spark集群中的某个Worker节点上的Executor,启动输入DStream的Receiver。
Receiver负责从外部数据源接收数据,Receiver接收到数据之后,会启动一个BlockGenerator,其会每隔一段时间(可配置,默认是200ms)将Receiver接收到的数据,打包成一个block,每个block除了会保存到所运行的Executor关联的BlockManager中之外,还会发送一份block信息如blockId到Driver端的ReceiverTracker上,其会将一个一个的block信息存入一个HashMap中,key就是时间。
记下来,JobGenerator会每隔我们定义的batch时间间隔,就会去ReceiverTracker中获取经过这个batch时间间隔内的数据信息blocks,将这些block聚合成一个batch,然后这个batch会被创建为一个RDD。
这样每隔一个batch时间间隔,都会将在这个时间间隔内接收的数据形成一个RDD,这样就会产生一个RDD序列,每个RDD代表数据流中一个时间间隔内的数据。正是这个RDD序列,形成SparkStreaming应用的输入DStream。
从宏观来说,Spark Streaming 使用“微批次”的架构,把流式计算当作一系列连续的小规模批处理来对待。Spark Streaming 从各种输入源中读取数据,并把数据分组为小的batch。新的batch按均匀的时间间隔创建出来。在每个时间区间开始的时候,一个新的batch就创建出来,在该区间内收到的数据都会被添加到这个batch中。在时间区间结束时,batch停止增长。时间区间的大小是由batch间隔这个参数决定的。batch间隔一般设在500 毫秒到几秒之间,由应用开发者配置。每个输入batch都形成一个RDD,以Spark 作业的方式处理并生成其他的RDD。处理的结果可以以批处理的方式传给外部系统。
StreamingContext初始化后,包括Receiver启动后,再到生成输入DStream,就完成了SparkStreaming应用程序的准备工作。
DStream的转化操作
在创建好输入DStream后,对其调用了filter()算子,filter()算子是转化操作,会将操作应用到DStream的每一个RDD。
一些常见的转化操作如下图
需要记住的是,尽管这些函数看起来像作用在整个流上一样,但事实上每个DStream 在内部是由许多RDD(批次)组成,且这些转化操作是分别应用到每个RDD 上的。例如,filter()会对DStream内的每个时间区间的数据(RDD)进行过滤,reduceByKey() 会归约每个RDD中的数据,但不会归约不同区间之间的数据。
对于本文的示例,对输入DStream作filter操作后生成新的DStream的过程如下:
Spark Streaming允许DStream的数据输出到外部系统,如数据库或文件系统,输出的数据可以被外部系统所使用,该操作类似于RDD的输出操作。
在Spark核心中,作业是由一系列具有依赖关系的RDD及作用于这些RDD上的算子函数所组成的操作链。在遇到行动操作时触发运行,向DAGScheduler提交并运行作业。Spark Streaming中作业的生成与Spark核心类似,对DStream进行的各种操作让它们之间构建起依赖关系。
当遇到DStream使用输出操作时,这些依赖关系以及它们之间的操作会被记录到名为DStreamGraph的对象中表示一个job。这些job注册到DStreamGraph并不会立即运行,而是等到Spark Streaming启动后,到达批处理时间时,才根据DSteamGraph生成job处理该批处理时间内接收的数据。在Spark Streaming如果应用程序中存在多个输出操作,那么在批处理中会产生多个job。
与RDD中的惰性求值类似,如果一个DStream及其派生出的DStream都没有被执行输出操作,那么这些DStream就都不会被求值。如果StreamingContext中没有设定输出操作,整个context就都不会启动。
常用的一种调试性输出操作是print(),它会在每个批次中抓取DStream的前十个元素打印出来。
一些常用的输出操作如下
总体来说,Spark Streaming是将流式计算分解成一系列短小的批处理作业。这里的批处理引擎是Spark Core,也就是Spark Streaming将输入数据按照batch interval(如5秒)分成一段一段的数据(Discretized Stream),每一段数据都转换成Spark中的RDD(Resilient Distributed Dataset),然后将Spark Streaming中对DStream的Transformation操作变为针对DSteam内各个RDD的Transformation操作,将RDD经过操作变成中间结果保存在内存中。整个流式计算根据业务的需求可以对中间的结果进行叠加或者存储到外部设备
假设batchInterval = 5s, SparkStreaming启动之后,0-5s内一直接受数据,假设SparkStreaming处理这一个批次数据的时间是3s,那么5-8s内一边接收新数据(开始第二批次),同时会触发DStream的job执行,这时会启动另一个线程处理第一批次的数据;8-10s内只是接收数据(还是第二批次);10-13s内一边接收新数据(开始第三批次),一边处理第二批次的数据,然后13-15s只是接收数据(还是第三批次),如此往复进行数据的接收与处理。
Spark Streaming相对其他流处理系统最大的优势在于流处理引擎和数据处理在同一个软件栈,其中Spark Streaming功能主要包括流处理引擎的数据接收与存储以及批处理作业的生成与管理,而Spark核心负责处理Spark Streaming发送过来的作业。
Spark Streaming分为Driver端和Client端,运行在Driver端为StreamingContext实例。该实例包括DStreamGraph和JobScheduler(包括ReceiverTracker和JobGenerator)等,而Client包括ReceiverSupervisor和Receiver等。
SparkStreaming进行流数据处理大致可以分为:启动流处理引擎、接收及存储流数据、处理流数据和输出处理结果等4个步骤,其运行架构如下
注意事项
1、local模式下需要启动至少两个线程,因为只开启了一条线程(这里只有接收数据的线程,却没有处理数据的线程),所以local模式下SparkStreaming必须至少设置两个线程
new SparkConf().setMaster("local[2]").setAppName("SimpleDemo");
2、Durations时间的设置–接收数据划分批次的时间间隔,多久触发一次job
new StreamingContext(conf, Seconds(1))
3、业务逻辑完成后,需要有一个输出操作,将SparkStreaming处理后的数据输出到外部存储系统
4、关于 StreamingContext 的 start()和 stop()
StreamingContext.start() //Spark Streaming应用启动之后是不能再添加业务逻辑
StreamingContext.stop() //无参的stop方法会将SparkContext一同关闭,解决办法:stop(false)
StreamingContext.stop() //停止之后是不能在调用start()
5、DStreams(Discretized Streams–离散的流),应用在每个DStream的算子操作,会应用在DStream内的各个RDD,进而应用在RDD的各个Partition,应用在Partition中的一条条数据,最终应用到每一条记录上
Spark简介
Spark
Spark Streaming简单入门(示例+原理)