Spark Streaming基础总结
最近在看Spark的相关东西,主要包括Spark的基础知识、Spark Streaming等,写了一些总结。
1 Introduction
从时间维度可以将数据分析可以分为历史数据的分析和实时数据的分析,例如Hive可以实现对于历史全量数据的计算,但是花费时间往往较长。实际场景中,如“双11”各大电商平台实时计算当前订单的情况时,需要实时对各个订单的数据依次进行采集、分析处理、存储等步骤,对于数据处理的速度要求很高,而且需要保持一段时期内不间断的运行。对于这类问题,Spark通过Spark Streaming组件提供了支持,Spark Streaming可以实现对于高吞吐量、实时的数据流的处理和分析,支持多样化的数据源如Kafka、Flume、HDFS、Kinesis和Twitter等。
2 DStream Model
类似于RDD之于Spark,Spark Streaming也有自己的核心数据对象,称为DStream(Discretized Stream,离散流)。使用Spark Streaming需要基于一些数据源创建DStream,然后在DStream上进行一些数据操作,这里的DStream可以近似地看作是一个比RDD更高一级的数据结构或者是RDD的序列。
虽然Spark Streaming声称是进行流数据处理的大数据系统,但从DStream的名称中就可以看出,它本质上仍然是基于批处理的。DStream将数据流进行分片,转化为多个batch,然后使用Spark Engine对这些batch进行处理和分析。
这里的batch是基于时间间隔来进行分割的,这里的__批处理时间间隔__(batch interval)需要人为确定,每一个batch的数据对应一个RDD实例。
2.1 Input DStream
Input DStream是一种特殊的DStream,它从外部数据源中获取原始数据流,连接到Spark Streaming中。Input DStream可以接受两种类型的数据输入:
(1)基本输入源:文件流、套接字连接和Akka Actor。
(2)高级输入源:Kafka、Flume、Kineis、Twitter等。
基本输入源可以直接应用于StreamingContext API,而对于高级输入源则需要一些额外的依赖包。
由于Input DStream要接受外部的数据输入,因此每个Input DStream(不包括文件流)都会对应一个单一的接收器(Receiver)对象,每个接收器对象都对应接受一个数据流输入。由于接收器需要持续运行,因此会占用分配给Spark Streaming的一个核,如果可用的核数不大于接收器的数量,会导致无法对数据进行其他变换操作。
2.2 DStream Transformation
DStream的转换也和RDD的转换类似,即对于数据对象进行变换、计算等操作,但DStream的Transformation中还有一些特殊的操作,如updateStateByKey()、transform()以及各种Window相关的操作。下面列举了一些主要的DStream操作:
Transformation | Interpretation |
---|---|
map(func) | 对DStream中的各个元素进行func函数操作,然后返回一个新的DStream. |
flatMap(func) | 与map方法类似,只不过各个输入项可以被输出为零个或多个输出项. |
filter(func) | 过滤出所有函数func返回值为true的DStream元素并返回一个新的DStream. |
repartition(numPartitions) | 增加或减少DStream中的分区数,从而改变DStream的并行度. |
union(otherStream) | 将源DStream和输入参数为otherDStream的元素合并,并返回一个新的DStream. |
count() | 通过对DStream中的各个RDD中的元素进行计数. |
reduce(func) | 对源DStream中的各个RDD中的元素利用func进行聚合操作,然后返回只有一个元素的RDD构成的新的DStream. |
countByValue() | 对于元素类型为K的DStream,返回一个元素为(K,Long)键值对形式的新的DStream,Long对应的值为源DStream中各个RDD的key出现的次数. |
reduceByKey(func, [numTasks]) | 利用func函数对源DStream中的key进行聚合操作,然后返回新的(K,V)对构成的DStream. |
join(otherStream, [numTasks]) | 输入为(K,V)、(K,W)类型的DStream,返回一个新的(K,(V,W))类型的DStream. |
cogroup(otherStream, [numTasks]) | 输入为(K,V)、(K,W)类型的DStream,返回一个新的 (K, Seq[V], Seq[W]) 元组类型的DStream. |
transform(func) | 通过RDD-to-RDD函数作用于DStream中的各个RDD,返回一个新的RDD. |
updateStateByKey(func) | 根据于key的前置状态和key的新值,对key进行更新,返回一个新状态的DStream. |
Spark Streaming提供了基于窗口(Window)的计算,即可以通过一个滑动窗口,对原始DStream的数据进行转换,得到一个新的DStream。这里涉及到两个参数的设定:
(1)窗口长度(window length):一个窗口覆盖的流数据的时间长度,必须是批处理时间间隔的倍数。窗口长度决定了一个窗口内包含多少个batch的数据。
(2)窗口滑动时间间隔(slide interval):前一个窗口到后一个窗口所经过的时间长度,必须是批处理时间间隔的倍数。
2.3 DStream Output Operations
DStream的输出操作(Output Operations)可以将DStream的数据输出到外部的数据库或文件系统。与RDD的Action类似,当某个Output Operation被调用时,Spark Streaming程序才会开始真正的计算过程。
下面列举了一些具体的输出操作:
Output Operations | Interpretation |
---|---|
print() | 打印到控制台. |
saveAsTextFiles(prefix, [suffix]) | 保存DStream的内容为文本文件,文件名为”prefix-TIME_IN_MS[.suffix]”. |
saveAsObjectFiles(prefix, [suffix]) | 保存DStream的内容为SequenceFile,文件名为 “prefix-TIME_IN_MS[.suffix]”. |
saveAsHadoopFiles(prefix, [suffix]) | 保存DStream的内容为Hadoop文件,文件名为”prefix-TIME_IN_MS[.suffix]”. |
foreachRDD(func) | 对Dstream里面的每个RDD执行func,并将结果保存到外部系统,如保存到RDD文件中或写入数据库. |
3 Fault Tolerance
容错(Fault Tolerance)指的是一个系统在部分模块出现故障时仍旧能够提供服务的能力。一个分布式数据处理程序要能够长期不间断运行,这就要求计算模型具有很高的容错性。
Spark操作的数据一般存储与类似HDFS这样的文件系统上,这些文件系统本身就有容错能力。但是由于Spark Streaming处理的很多数据是通过网络接收的,即接收到数据的时候没有备份,为了让Spark Streaming程序中的RDD都能够具有和普通RDD一样的容错性,这些数据需要被复制到多个Worker节点的Executor内存中。
Spark Streaming通过检查点(Check Point)的方式来平衡容错能力和代价问题。DStream依赖的RDD是可重复的数据集,每一个RDD从建立之初都记录了每一步的计算过程,如果RDD某个分区由于一些原因数据丢失了,就可以重新执行计算来恢复数据。随着运行时间的增加,数据恢复的代价也会随着计算过程而增加,因此Spark提供了检查点功能,定期记录计算的中间状态,避免数据恢复时的漫长计算过程。Spark Streaming支持两种类型的检查点:
(1)元数据检查点。这种检查点可以记录Driver的配置、操作以及未完成的批次,可以让Driver节点在失效重启后可以继续运行。
(2)数据检查点。这种检查点主要用来恢复有状态的转换操作,例如updateStateByKey或者reduceByKeyAndWindow操作,它可以记录数据计算的中间状态,避免在数据恢复时,长依赖链带来的恢复时间的无限增长。
开启检查点功能需要设置一个可靠的文件系统路径来保存检查点信息,代码如下:
streamingContext.checkpoing(checkPointDirectory) //参数是文件路径
为了让Driver自动重启时能够开启检查点功能,需要对原始StreamingContext进行简单的修改,创建一个检查检查点目录的函数,代码如下:
def functionToCreateContext(): StreamingContext = {
val ssc = new StreamingContext(...)
ssc.checkpoint(checkpointDirecroty) //设置检查点目录
...
val lines = ssc.socketTextStream(...) //创建DStream
}
//从检查点目录恢复一个StreamingContext或者创建一个新的
val ssc = StreamingContext.getOrCreate(checkpointDirectory,
functionToCreateContext())
//启动context
context.start()
context.awaitTermination()
参考文献
[1] 于俊. Spark核心技术与高级应用[M]. 机械工业出版社, 2016.
[2] 陈欢林世飞. Spark最佳实践[M].人民邮电出版社, 2016.