Spark Streaming
Spark Streaming
简介
Spark Streaming
是Spark
为了处理实时流数据而设计的模型,允许基于批处理API
进行对实时流数据进行处理。Spark Streaming
使用离散化流(discretized stream)
作为抽象表示,叫做DStream
。类似于Spark
中的RDD
,用于存储实时流数据。DStream
是将实时流数据分批整合成RDD
,是RDD
组成的序列。所以DStream
支持转化操作(RDD之
间的转换)与输出操作(将数据写入外部系统)。Spark Streaming
因为是处理实时流数据,所以需要保证7*24
不间断工作,为了保证任务的容错性,会提供检查点机制。
Spark Streaming
架构与抽象
-
Spark Streaming
采用微批次的架构作为处理流数据的方式。Spark Streaming
从各种输入源中读取数据,每个新的批次是一个时间间隔内接收到的数据,一个批次的数据作为一个RDD
对象,可以通过控制批次间隔的参数控制每次批处理数据RDD
的大小。一般是几百毫秒到几秒之间。
graph LR
source[输入源]
subgraph Streaming
B[接收器] --> C[DStream]
end
source --> B
C --> D[输出结果]
-
DStream
数据结构
DStream
时间0到1的数据
时间1到2的数据
时间2到3的数据
时间3到4的数据
时间4到5的数据
Spark Streaming
转化操作
-
Spark Streaming
根据批次处理是否依赖于之前批次数据的条件,将转化操作分为有状态(依赖)和无状态(不依赖)两种。
无状态
- 无状态转化操作可以看做是分别对每个批次的
RDD
单独操作,每个批次之间相互独立。 - 支持的常见
API
函数名称 | 目的 | 示例 |
| 转换元素格式或取值 |
|
| 元素切分为多个元素 |
|
| 返回制定条件的元素 |
|
| 修改分区数 |
|
| 键值对数据类型处理相同 |
|
| 键值对数据类型相同 |
|
有状态
- 窗口时长:每次计算最近多少个批次的数据,如果一个批次的时间间隔为10秒,如果窗口时长为30表示每次计算3个批次的数据。
- 滑动步长:滑动步长默认值与批次间隔一样,表示每隔多少时间,进行一次计算,如果期望每隔两个批次计算一次,则滑动步长时间为批次间隔的二倍。
- 窗口时长为30秒,滑动步长为20秒,批次间隔为10秒的数据处理示意图:(每隔两个批次对前三个批次进行计算)
t1
r1
t2
r2
t3
t4
r3
t5
t6
- 常用
API
简介
//linesTimes数据格式为 (key,1)表示流数据获取中每个key值出现的个数 默认为1
val lineTimes = lines.map(x => (x,1))
lineTimes.reduceByKeyAndWindow(
{(x,y) => x+y}, //对新入窗口的批次 相同key次数相加
{(x,y) => x-y}, //对离开窗口的批次 相同key次数相减
Seconds(30), //窗口时长30秒 6个批次
Seconds(5) //滑动步长5秒 每隔一次批次就计算一次
)
API | 含义 |
| 返回每个窗口中元素个数 |
| 返回每个窗口中值对应的个数 |
updateStateByKey()
操作键值对格式的DStream
,并进行无限增长的计算。
def main(args: Array[String]): Unit = {
/*
values : 表示当前批次中相同key值 的value值集合
state : 表示当前key值的之前状态
*/
def updateStateMethod(values:Seq[Int],state:Option[Int]) ={
//创建一个变量,用于记录单词出现次数
var newValue=state.getOrElse(0) //getOrElse相当于if....else.....
for(value <- values){
newValue +=value //将单词出现次数累计相加
}
Option(newValue)
}
//连接nc(netcat)服务,接收数据源,产生Dtream 对象
val lines=ssc.socketTextStream("localhost",7777)
//分隔单词,并将分隔后的每个单词出现次数记录为1
val pairs=lines.flatMap(_.split(" "))
.map(word=>(word,1))
//调用updateStateByKey算子,统计单词在全局中出现的次数
val result = pairs.updateStateByKey[Int](updateStateMethod)
//result : DStream[scala.Tuple2[String, Int]]
ssc.start()
ssc.awaitTermination()
}
Spark Streaming
输出操作
print()
默认打印每个批次的前10行
saveAsTextFiles("outputDir", "txt")
将内容按批次保存成文本文件
foreachRDD{}
获取每个批次RDD
,将RDD
自定义存储到外部系统
7*24
不间断工作
检查点机制
为了保证Spark Streaming
的容错性,我们将Spark Streaming
中的应用数据阶段性的存储到可靠的文件系统中。即便Spark
自带谱系图实现RDD
数据丢失的容错性,Spark Streaming
还是从性能角度设置了检查点机制,直接将获取的RDD
数据保存在可靠的文件系统中,保证数据的容错性。
ssc.checkpoint("文件系统路径")
驱动器程序容错
def createStreamingContext(): StreamingContext = {
val sc = new SparkContext(conf)
val ssc = new StreamingContext(sc, Seconds(1))
ssc.checkpoint(checkpointDir)
ssc
}
val ssc = StreamingContext.getOrCreate(checkpointDir, createStreamingContext)
- 当驱动器程序失败后,重启驱动器程序,
getOrCreate
方法会从检查点目录的位置初始化StreamingContext
对象,继续处理。
工作节点容错
- 所有从外部节点获取到的数据会在多个节点中备份,包括基于转化操作生成的
RDD
都允许一个节点的失败。
接收器容错
- 当接收器节点上的发生错误,
Spark Streaming
会在别的节点上重启失败的接收器。对于重启过程中接收失败的数据,是否会丢失,取决于上游数据源是否具有事务机制。
处理保证
- 因为任务某种原因,输出操作会执行多次,这时需要保证输出到外部系统的代码部分具有事务,保证同一条数据在多次插入过程中生成一条结果。例如,向
mysql
插入数据时,先查询唯一列的值是否存在。
性能考量
批次与窗口大小
- 当处理时间不变时尽量使用更小的批次,保证内存的可用性。一般情况下
500毫秒
为较好的最小批次大小。10秒
为一个较大的批次大小,
并行度
- 增加接收器数量
创建多个DStream
(每个DStream
会创建一个新的接收器),然后使用union
将数据合并为同一个数据源。
- 将接收到的数据显式分区
DStream.repartition(分区数)
- 提高聚合计算的并行度
val init = sc.parallelize(List("1 2","3 4","3 6"))
val kv = init.map(x => (x.split(" ")(0).toInt, x.split(" ")(1).toInt))
//(1,2),(3,10)
println(kv.reduceByKey((x, y) => x+y, 10).collect().mkString(","))