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
数据结构Spark Streaming
转化操作Spark Streaming
根据批次处理是否依赖于之前批次数据的条件,将转化操作分为有状态(依赖)和无状态(不依赖)两种。无状态转化操作可以看做是分别对每个批次的RDD
单独操作,每个批次之间相互独立。
支持的常见API
函数名称 | 目的 | 示例 |
---|---|---|
map() |
转换元素格式或取值 | ds.map(x => x+1) |
flatMap() |
元素切分为多个元素 | ds.flatMap(x => x.split(" ")) |
filter() |
返回制定条件的元素 | ds.filter(x => x != "") |
repartition() |
修改分区数 | ds.repartition(10) |
reduceByKey() |
键值对数据类型处理相同key 值 |
ds.reduceByKey((x,y) => x+y) |
groupByKey() |
键值对数据类型相同key 的值分组 |
ds.groupByKey() |
窗口时长:每次计算最近多少个批次的数据,如果一个批次的时间间隔为10秒,如果窗口时长为30表示每次计算3个批次的数据。
滑动步长:滑动步长默认值与批次间隔一样,表示每隔多少时间,进行一次计算,如果期望每隔两个批次计算一次,则滑动步长时间为批次间隔的二倍。
窗口时长为30秒,滑动步长为20秒,批次间隔为10秒的数据处理示意图:(每隔两个批次对前三个批次进行计算)
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 | 含义 |
---|---|
countByWindow() |
返回每个窗口中元素个数 |
countByValueAndWindow() |
返回每个窗口中值对应的个数 |
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(","))