import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Durations, StreamingContext}
//定义一个对象用于保存key对应的值,和值的状态
case class StreamingStateValue(var value:Int,var isUpdate:Boolean = false)
object SparkStreamingSocketPortUpdateStateObject {
def main(args: Array[String]): Unit = {
val checkPoint = "/home/xiangyongqiao15/mydata/"
val createFunction: () => StreamingContext = () => {
val conf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketPort").setMaster("local[2]")
val streamingContext = new StreamingContext(conf, Durations.seconds(5))
//使用updateStateByKey必须得设置一个checkPoint地址用于保存历史的数据
streamingContext.checkpoint(checkPoint)
//在有checkpoint数据的时候,修改地址和端口是不生效的
val lines: ReceiverInputDStream[String] = streamingContext.socketTextStream("192.168.88.189", 9876)
val flatMap: DStream[String] = lines.flatMap(_.split(" "))
//在rdd之间传输包含了值的StreamingStateValue对象
val mapToPair: DStream[(String, StreamingStateValue)] = flatMap.map((_, StreamingStateValue(1)))
val reduceByKey: DStream[(String, StreamingStateValue)] = mapToPair.reduceByKey((a,b) => StreamingStateValue(a.value + b.value))
val updateStateByKey: DStream[(String, StreamingStateValue)] = reduceByKey.updateStateByKey(
//这个参数a是本批次的数据,b是这个key上次的历史结果
(a: Seq[StreamingStateValue], b: Option[StreamingStateValue]) => {
var total = 0
for (i <- a) {
total += i.value
}
//这里为什么要判断一下,因为这个key有可能是第一次进入,也就是说没有历史数据,那此时应该给个初始值0
val last:StreamingStateValue = if (b.isDefined) b.get else StreamingStateValue(0)
//好果本批次key有更新的话,那这个key对应的StreamingStateValue对象的状态改为true
if(a.size != 0){
last.isUpdate = true
}else{
last.isUpdate = false
}
val now = last.value + total
last.value = now
//使用这个修改了状态的对象StreamingStateValue做为本批次这个key的结果
Some(last)
})
//在有checkpoint数据的时候,修改算子中的function是有效的
//所以使用了checkpoint恢复streamingContext的程序,这样的程序并不影响你后续的升级代码
updateStateByKey.foreachRDD((r, t) => {
//可以用rdd的isEmpty方法,判断此批次是否有数据,如果有数据再执行
if (!r.isEmpty()) {
println(s"count time:${t},${r.collect().toList}")
//过滤状态为true,代表本批次有更新的数据,这样的话比如插入数据库就不会每次都全量插入
//只插入每批次有变化的数据就可以了
val filter: RDD[(String, StreamingStateValue)] = r.filter(_._2.isUpdate)
println(s"count time:${t},filter data:${filter.collect().toList}")
}
})
streamingContext
}
//使用getOrCreate从checkPoint里恢复最后一次的StreamingContext状态,如果没有checkPoint地址,那就新建一个StreamingContext
val strc: StreamingContext = StreamingContext.getOrCreate(checkPoint, createFunction)
//这里必须得使用getOrCreate判断好(要么是历史的,要么是新create的)的那个streamingContext进行启动
strc.start()
strc.awaitTermination()
}
}
Spark Streaming 在监控端口时,对数据进行标记
最新推荐文章于 2021-01-17 14:06:01 发布