各种算子请查看官网:SparkStreaming
下面列举常用几个示例:
1. foreachRDD
- output operation 算子,必须对抽取出来的 RDD 执行 action 类算子
代码才能执行。
import org.apache.spark.SparkConf
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Durations, StreamingContext}
object ForeachRDDTest {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local[2]")
conf.setAppName("test")
val ssc = new StreamingContext(conf, Durations.seconds(5))
val lines = ssc.socketTextStream("node4", 9999)
val result: DStream[(String, Int)] = lines.flatMap(line => {
// println("************"+line)
line.split(" ")
}).map(word => {
new Tuple2(word, 1)
}).reduceByKey((v1, v2) => {
v1 + v2
})
/**
* foreachRDD :
* 1).foreachRDD 可以获取DStream中的RDD,可以对RDD使用RDD的transformation类算子进行转换,
* 但是一定要对RDD进行Actioin算子触发,不然DStream的逻辑也不会执行。
* 2).foreachRDD算子内的代码,获取的RDD算子外的代码是在Driver端执行的,每隔batchInterval执行一次。可以利用这个特点动态的改变广播变量
*/
result.foreachRDD(rdd => {
println("**** Driver ****")
val bc: Broadcast[String] = rdd.sparkContext.broadcast("读取数据库mysql中的数据")
val map: RDD[String] = rdd.map(tp => {
println("===== Executor-map =====" + tp)
val value: String = bc.value
tp._1 + "-" + tp._2
})
val end = map.filter(s => {
true
})
end.foreach(println) //进行Actioin算子触发,确保RDD中的逻辑执行
})
ssc.start()
ssc.awaitTermination()
}
}
2. transform
- transformation 类算子
- 可以通过 transform 算子,对 Dstream 做 RDD 到 RDD 的任意操
作。
代码示例:
import org.apache.spark.{SparkConf}
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* transform :
* 1).DStream的Transformation算子,可以获取DStream中的RDD,对RDD进行RDD的Transformation类算子转换,也可以使用Action算子。
* 但是一定最后需要返回一个RDD,将返回的RDD会包装到一个DStream中。
* 2).与foreachRDD类似,算子内的代码获取的RDD算子外的代码是在Driver端执行的,可以通过这个算子来动态的改变广播变量的值
*/
object TestTransform {
def main(args: Array[String]): Unit = {
val context: StreamingContext = new StreamingContext(new SparkConf()
.setMaster("local[2]")
.setAppName("TestTransform"), Durations.seconds(5))
val lines = context.socketTextStream("node4", 9999)
lines.transform(rdd => {
/**
* 与foreachRDD类似,算子内的代码获取的RDD算子外的代码是在Driver端执行的,
* 可以通过这个算子来动态的改变广播变量的值
*/
println("***** Driver ******")
rdd.filter(r => !r.contains("zhangsan"))
}).print()
context.start()
context.awaitTermination()
}
}
3. updateStateByKey算子
- transformation 算子
- updateStateByKey 作用:
- 为 SparkStreaming 中每一个 Key 维护一份 state 状态,state
类型可以是任意类型的,可以是一个自定义的对象,更新函数也
可以是自定义的。 - 通过更新函数对该 key 的状态不断更新,对于每个新的 batch 而
言,SparkStreaming 会在使用 updateStateByKey 的时候为已
经存在的 key 进行 state 的状态更新。
- 使用到 updateStateByKey 要开启 checkpoint 机制和功能。
- 多久会将内存中的数据写入到磁盘一份?
如果 batchInterval 设置的时间小于 10 秒,那么 10 秒写入磁盘一
份。如果 batchInterval 设置的时间大于 10 秒,那么就会
batchInterval 时间间隔写入磁盘一份。
代码示例:
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* updataeStateByKey :
* 1).可以更新key的状态,统计自从SparkStreaming 启动以来所有key的状态值
* 2).需要设置checkpoint 来保存之前所有key对应的value状态值
* 默认状态是在内存中的,必须设置checkpoint保存状态,多久将内存中的装填向checkpoint持久化一次?
* 如果batchInterval 小于10s ,那就10s保存一次,如果batchInterval 大于10s 就batchInterval 保存一次,为了避免频繁的磁盘IO。
*/
object UpdateStateByKey {
def main(args: Array[String]): Unit = {
val context: StreamingContext = new StreamingContext(new SparkConf()
.setMaster("local[2]")
.setAppName("UpdateStateByKey"), Durations.seconds(5))
context.sparkContext.setLogLevel("Error")
context.checkpoint("T:/code/spark_scala/data/sparkstreaming/ck")
val lines: ReceiverInputDStream[String] = context.socketTextStream("node4", 9999)
val words: DStream[String] = lines.flatMap(line => {
line.split(" ")
})
val pairWords: DStream[(String, Int)] = words.map(word => {
(word, 1)
})
/**
* updateStateByKey : 按照key 分组,针对每个分组内的数据进行处理
* seq: 当前组内的value组成的集合
* option : 当前批次之前批次对应某个key对应的值
*/
pairWords.updateStateByKey((seq: Seq[Int], option: Option[Int]) => {
var value = option.getOrElse(0)
for (currentValue <- seq) {
value += currentValue
}
Option(value)
}).print()
context.start()
context.awaitTermination()
}
}
结果:
-------------------------------------------
Time: 1604658690000 ms
-------------------------------------------
(hello,2) // 之前输入的key状态会被保留。再输入时value+现在组内value的数量
(spark,1)
(人在囧途,1)
4. SparkStreaming renduceByKeyAndWindow算子
4.1
示意图:
官网示意图;
示例代码:
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* 需求:
* 每隔一段时长,统计最近一段时间数据。
* reduceByKeyAndWindow: 每隔一段时长统计最近一段时间的内的数据。
* 注意:
* 窗口长度 - window length - wl
* 滑动间隔 - slding interval - si
* 窗口长度和滑动间隔必须是batchInterval的整数倍
*/
object ReduceByKeyAndWindow1 {
def main(args: Array[String]): Unit = {
val context: StreamingContext = new StreamingContext(new SparkConf()
.setMaster("local[2]")
.setAppName("ReduceByKeyAndWindow1"), Durations.seconds(5))
context.sparkContext.setLogLevel("Error")
val lines = context.socketTextStream("node4", 9999)
lines.flatMap(line => line.split(" "))
.map((_, 1))
.reduceByKeyAndWindow((v1: Int, v2: Int) => {
v1 + v2
// 每隔 滑动间隔 统计最近 窗口长度内的数据,按照指定逻辑计算。
}, Durations.seconds(15), Durations.seconds(5))
.print()
context.start()
context.awaitTermination()
}
}
在服务器上输入:
[root@node4 ~]# nc -lk 9999
自强不息
结果显示3个batchInterval即一个窗口长度的时间
Time: 1604660660000 ms
-------------------------------------------
(自强不息,1)
-------------------------------------------
Time: 1604660665000 ms
-------------------------------------------
(自强不息,1)
-------------------------------------------
Time: 1604660670000 ms
-------------------------------------------
(自强不息,1)
-------------------------------------------
Time: 1604660675000 ms
-------------------------------------------
4.2 SparkStreaming renduceByKeyAndWindow 优化处理方式原理
官网截图:
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* reduceByKeyAndWindow : 优化方式处理数据,在集群处理数据能力不足时使用
* 1).reduceByKeyAndWindow(fun1,fun2,窗口长度,滑动间隔)
* 2).优化方式需要设置checkpoint 保存状态
* 3).进来k对应的value值减去出去key对应的value值,保存其状态
*/
object ReduceByKeyAndWindow2 {
def main(args: Array[String]): Unit = {
val context: StreamingContext = new StreamingContext(new SparkConf()
.setMaster("local[2]")
.setAppName("ReduceByKeyAndWindow2"), Durations.seconds(5))
context.sparkContext.setLogLevel("Error")
context.checkpoint("T:/code/spark_scala/data/sparkstreaming/ck")
val lines = context.socketTextStream("node4", 9999)
lines.flatMap(line => line.split(" "))
.map((_, 1))
.reduceByKeyAndWindow((v1: Int, v2: Int) => {
v1 + v2
// 每隔 滑动间隔 统计最近 窗口长度内的数据,按照指定逻辑计算。
}, (v1: Int, v2: Int) => {
v1 - v2
}, Durations.seconds(15), Durations.seconds(5))
.print()
context.start()
context.awaitTermination()
}
}
4.3 window 窗口函数 ,可以每隔一段时间,将最近的窗口长度内的数据组成一个DStream处理。
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* window 窗口函数 ,可以每隔一段时间,将最近的窗口长度内的数据组成一个DStream处理。
*/
object WindowTest {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local[2]")
conf.setAppName("test")
val ssc = new StreamingContext(conf,Durations.seconds(5))
ssc.sparkContext.setLogLevel("Error")
val lines = ssc.socketTextStream("mynode5",9999)
val blackList = List[String]("zhangsan","lisi","wangwu")
//window 窗口函数
val windowDs: DStream[String] = lines.window(Durations.seconds(15),Durations.seconds(5))
//输入数据 hello xxx
val result = windowDs.filter(one => {
!blackList.contains(one.split(" ")(1))
})
result.print()
ssc.start()
ssc.awaitTermination()
}
}
5.textFileStream 算子
可以监控目录下的增量文件数据,增加的数据格式必须是text格式,并且目录下已经存在的数据不能监控到,已经存在的数据追加数据也不能被监控到。
只能监控原子产生在目录下的数据。
6. saveAsTextFile算子
saveAsTextFile : 将数据结果保存到目录中,可以指定前缀后缀,多级目录可以指定在前缀中。
代码示例:
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* textFileStream :
* 可以监控目录下的增量文件数据,增加的数据格式必须是text格式,并且目录下已经存在的数据不能监控到,已经存在的数据追加数据也不能被监控到。
* 只能监控原子产生在目录下的数据。
*
* saveAsTextFile : 将数据结果保存到目录中,可以指定前缀后缀,多级目录可以指定在前缀中。
*
*/
object StreamingMonitorDIR {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local")
conf.setAppName("test")
val ssc = new StreamingContext(conf,Durations.seconds(5))
val lines: DStream[String] = ssc.textFileStream("./data/streamingCopyFile")
lines.flatMap(line=>{line.split(" ")})
.map(word=>{(word,1)})
.reduceByKey((v1,v2)=>{v1+v2})
.saveAsTextFiles("./data/streamingresult/aaa","bbb")
ssc.start()
ssc.awaitTermination()
}
}