DataFrame和SQL操作
可以方便的对刘数据使用DataFrame和SQL操作。在 StreamingContext使用的SparkContext构建一个SparkSession。这样driver失败时可以重启。下面的例子对此前的word count例子进行了修改,使用DataFrames和SQL来对词进行计数。每一个RDD转化成一个DataFrame,登记为临时表格并用SQL查询。
# Lazily instantiated global instance of SparkSession
def getSparkSessionInstance(sparkConf):
if ("sparkSessionSingletonInstance" not in globals()):
globals()["sparkSessionSingletonInstance"] = SparkSession \
.builder \
.config(conf=sparkConf) \
.getOrCreate()
return globals()["sparkSessionSingletonInstance"]
...
# DataFrame operations inside your streaming program
words = ... # DStream of strings
def process(time, rdd):
print("========= %s =========" % str(time))
try:
# Get the singleton instance of SparkSession
spark = getSparkSessionInstance(rdd.context.getConf())
# Convert RDD[String] to RDD[Row] to DataFrame
rowRdd = rdd.map(lambda w: Row(word=w))
wordsDataFrame = spark.createDataFrame(rowRdd)
# Creates a temporary view using the DataFrame
wordsDataFrame.createOrReplaceTempView("words")
# Do word count on table using SQL and print it
wordCountsDataFrame = spark.sql("select word, count(*) as total from words group by word")
wordCountsDataFrame.show()
except:
pass
words.foreachRDD(process)
也可以在不同线程流数据定义的表格上运行SQL查询(与运行的StreamingContext不同步)。记住将StreamingContext设置成能记住足够的流数据量以便查询可以运行。否则StreamingContext不知道任何非同步SQL查询,会在查询完成前把旧的流数据删除。例如,你想查询最后一批次,但查询花了5分钟运行,那么应调用streamingContext.remember(Minutes(5))。
MLlib操作
也可以方便的使用MLlib提供的机器学习算法。首先,有一些流机器学习算法(例如,流线性回归,流K平均等)可以同时从流数据学习并应用到流数据模型。除了这些,对于其他的机器学习算法可以离线学习(例如使用历史数据)然后应用于在线的流数据模型。
缓存/驻留
与RDDs相似,DStreams允许开发者将流数据驻留在内存。使用persist()方法会自动将DStream中每个RDD保存在内存。这对于DStream中需要多次计算的数据很有用(对同一数据的多个操作)。对于基于窗口的操作如reduceByWindow和reducyByKeyAndWindow,以及基于状态的updateStateByKey也是如此。因此基于窗口操作生成的DStream自动驻留在内存,不需要开发者单独调用persist()。
对于通过网络接收数据的输入流(例如Kafka,Flume,端口等)默认的驻留级别是设置将数据在两个节点重复以实现容错。
注意,与RDDs不同,DStreams默认驻留级别将数据在内存中序列化。
检查点
一个流应用需要24/7运行因此对于与应用逻辑(例如系统错误,或JVM崩溃等)无关的失败要保持弹性。因此Spark Stream需要使用检查点来检查足够的信息到容错储存系统这样在失败时才能恢复。有两类数据会被检查:
Metadata——保存定义流计算到容错存储如HDFS的信息。这倍用来恢复运行流应用driver节点的失败。包括设置,DStream操作,不完整的批次。
数据——将生成的RDDs保存到可靠的储存空间。这对一些跨批次数据结合等状态转化是必要的。在这些转化中,生成的RDDs依赖于此前批次的RDDs,导致依赖链随时间增长而延长。为避免恢复时间的无限延长(与依赖链成比例),状态转化的中间RDDs定期被检查并存储到可靠的存储容器如HDFS以切断依赖链。
当需要以下功能时要使用检查点:
使用状态转化——如果使用updateStateByKey或者reduceByKeyAndWindow,要提供检查点文件夹。
从运行应用driver失败中恢复——Metadata检查点被用来恢复进程信息。
如何设置检查点
检查点可以通过在容错、可靠的文件系统如HDFS,S3等设置存放检查信息的文件夹实现。使用streamingContext.checkpoint(checkpointDirectory)。
# Function to create and setup a new StreamingContext
def functionToCreateContext():
sc = SparkContext(...) # new context
ssc = StreamingContext(...)
lines = ssc.socketTextStream(...) # create DStreams
...
ssc.checkpoint(checkpointDirectory) # set checkpoint directory
return ssc
# Get StreamingContext from checkpoint data or create a new one
context = StreamingContext.getOrCreate(checkpointDirectory, functionToCreateContext)
# Do additional setup on context that needs to be done,
# irrespective of whether it is being started or restarted
context. ...
# Start the context
context.start()
context.awaitTermination()
累积器,广播变量和检查点
在Spark Streaming中累积器和广播变量无法从检查点恢复,需要单独设置。
def getWordBlacklist(sparkContext):
if ("wordBlacklist" not in globals()):
globals()["wordBlacklist"] = sparkContext.broadcast(["a", "b", "c"])
return globals()["wordBlacklist"]
def getDroppedWordsCounter(sparkContext):
if ("droppedWordsCounter" not in globals()):
globals()["droppedWordsCounter"] = sparkContext.accumulator(0)
return globals()["droppedWordsCounter"]
def echo(time, rdd):
# Get or register the blacklist Broadcast
blacklist = getWordBlacklist(rdd.context)
# Get or register the droppedWordsCounter Accumulator
droppedWordsCounter = getDroppedWordsCounter(rdd.context)
# Use blacklist to drop words and use droppedWordsCounter to count them
def filterFunc(wordCount):
if wordCount[0] in blacklist.value:
droppedWordsCounter.add(wordCount[1])
False
else:
True
counts = "Counts at time %s %s" % (time, rdd.filter(filterFunc).collect())
wordCounts.foreachRDD(echo)
部署应用
运行Spark Streaming应用,需要满足以下条件:
-有集群管理器的集群
-将应用打包成JAR
-为执行器分配足够的内存
-配置检查点
-配置应用驱动器的自动重启
-配置write ahead日志
-设定最大接收率
升级应用代码
如果一个正在运行的Spark Streaming应用需要升级新的应用代码,有两种方式:
-升级后的应用启动并与现存的应用并行运行。当新的应用与旧的应用接收相同数据时,可以将旧的应用下线。这需要数据源可以将数据发送到两个目标。
-现有应用接收完整数据后关闭(StreamingContext.stop(...)或者JavaStreamingContext.stop(...)),然后启动新的应用。这需要输入源提供缓存(如
Kafka和Flume)。
监测应用
除了Spark的监测能力外,Spark Streaming提供额外的监测。当使用StreamingContext时, Spark web UI显示一个Streaming标签,里面有关于正在运行接收器的统计信息(是否活跃、接收记录数量、接收器错误等) 以及完成的批次(批次处理时间,排队延迟等)。
处理时间和计划延迟指标非常重要。当批次处理时间持续大于批次间隔并且/或排队延迟持续增加,意味着系统的处理速度跟不上批次产生速度,这时应考虑降低批次处理时间。
性能调试
在高层,要考虑两个方面:
-有效使用集群资源降低每批数据处理时间
-设定合理的批次规模,批次的接收和处理可以同步
从网络接收数据需要将数据非序列化以存储到Spark。如果数据接收成为系统的一个瓶颈,则考虑将数据接收并行化。
numStreams = 5
kafkaStreams = [KafkaUtils.createStream(...) for _ in range (numStreams)]
unifiedStream = streamingContext.union(*kafkaStreams)
unifiedStream.pprint()
其他需要考虑的有:降低批次处理时间、数据处理的并行层度、数据序列化、任务发起成本、设置合理的批次间隔、内存细调等,详见http://spark.apache.org/docs/latest/tuning.htm