DStream的转化和输出
1.无状态的转化
处理只关心当前采集周期内的数据,上个周期采集到的数据不做保留和处理
2.有状态的转化
如上图,第一个采集周期5s内的数据,如果需要拿去和第二个5s内的数据进行交互,就叫做有状态的转换。否则就是无状态的操作。
例子:依旧是统计单词的个数,之前只能统计一个采集周期内的单词的个数,现在可以统计所有的单词的个数。
需要注意的点:
- 使用updateStateByKey来完成有状态的统计
- 要设定检查点存储的位置
object updata {
def main(args: Array[String]): Unit = {
//使用sparkstreaming来完成wordcount
//spark的配置对象
val conf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
//实时分析的环境对象
//采集周期:以指定的时间为周期采集实时数据
val streamingContext = new StreamingContext(conf, Seconds(5))
//保存数据的状态,需要设定检查点路径
streamingContext.sparkContext.setCheckpointDir("cp")
//从指定的端口中采集数据
val socketLineDStream: ReceiverInputDStream[String] = streamingContext.socketTextStream("hadoop102", 9999)
//将采集的数据进行分解
val wordDStream = socketLineDStream.flatMap(line => line.split(" "))
//将数据进行结构的转变进行统计分析
val mapDStream = wordDStream.map((_, 1))
//将转换结构后的数据进行聚合处理
//无状态的情况
//val wordToSumDStream = mapDStream.reduceByKey(_ + _)
//这里使用updateStateByKey来完成有状态的单词统计
val stateDStream: DStream[(String, Int)] = mapDStream.updateStateByKey {
case (seq, buffer) => {
val sum = buffer.getOrElse(0) + seq.sum
Option(sum)
}
}
//将结果打印
stateDStream.print()
//结束,不需要stop(),因为是流式数据,需要一直采集下去。
//要完成采集器不停,整个程序不能停的效果
//启动采集器
streamingContext.start()
//Driver等待采集器的执行
streamingContext.awaitTermination()
}
}
3.Window Operations
窗口函数介于有状态和无状态之间,根据window(窗口大小,步长)两个参数来修改处理参数的范围(是几个采集周期,移动是怎么移动的),这两个参数的大小都要是采集周期的整数倍。
使用这个函数可以明显的看出数据在时间的流动过程中的变化,适合用来分析带有时间属性的流动数据。
使用的时候只要设定一下window()函数即可。
object Windows {
def main(args: Array[String]): Unit = {
//使用sparkstreaming来完成wordcount
//spark的配置对象
val conf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
//实时分析的环境对象
//采集周期:以指定的时间为周期采集实时数据
val streamingContext = new StreamingContext(conf, Seconds(3))
//从指定的端口中采集数据
val socketLineDStream: ReceiverInputDStream[String] = streamingContext.socketTextStream("hadoop102", 9999)
//窗口大小应该是采集周期的整数倍,步长也要采集周期的整数被
val windowDStream = socketLineDStream.window(Seconds(9),Seconds(3))
//将采集的数据进行分解
val wordDStream = windowDStream.flatMap(line => line.split(" "))
//将数据进行结构的转变进行统计分析
val mapDStream = wordDStream.map((_, 1))
//将转换结构后的数据进行聚合处理
val wordToSumDStream = mapDStream.reduceByKey(_ + _)
//将结果打印
wordToSumDStream.print()
//结束,不需要stop(),因为是流式数据,需要一直采集下去。
//要完成采集器不停,整个程序不能停的效果
//启动采集器
streamingContext.start()
//Driver等待采集器的执行
streamingContext.awaitTermination()
}
}
4.transform
transform也是用来做RDD的转换的,它和map的区别如下图:
transform可以在内层实现每隔采集周期就进行一次Driver的操作,这是map做不到的。
5.Join
连接操作(leftOuterJoin, rightOuterJoin, fullOuterJoin也可以),可以连接Stream-Stream,windows-stream to windows-stream、stream-dataset
底层实现就是join()
6.DStream的输出
输出操作如下:
- print():在运行流程序的驱动结点上打印DStream中每一批次数据的最开始10个元素。这用于开发和调试。在Python API中,同样的操作叫print()。
- saveAsTextFiles(prefix,[suffix]):以text文件形式存储这个DStream的内容。每一批次的存储文件名基于参数中的prefix和suffix。
- saveAsObjectFiles(prefix, [suffix]):以Java对象序列化的方式将Stream中的数据保存为
SequenceFiles 。Python中目前不可用。 - saveAsHadoopFiles(prefix, [suffix]):将Stream中的数据保存为 Hadoop files.
Python API Python中目前不可用。 - foreachRDD(func):这是最通用的输出操作,即将函数 func 用于产生于stream的每一个RDD。其中参数传入的函数func应该实现将每一个RDD中数据推送到外部系统,如将RDD存入文件或者通过网络将其写入数据库。注意:函数func在运行流应用的驱动中被执行,同时其中一般函数RDD操作从而强制其对于流RDD的运算。
这个得到是stream中的每个RDD,可以在fun中对这些RDD进行操作。
tips:在进行一些数据库的连接时
- 连接不能写在driver层面:connection无法序列化
- 如果写在foreach则每个RDD都创建,得不偿失;
- 增加foreachPartition,在分区创建。