有状态算子、滑动窗口和滚动窗口
有状态算子
之前我们在运行 Spark Streaming 的时候发现微批处理之中,每一个批次都是相对独立的
如何让其能够产生累加的效果呢?
package com.shujia.stream
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Durations, StreamingContext}
object Demo2UpdateStateByKey {
def main(args: Array[String]): Unit = {
/**
* 1、创建SparkContext
*
*/
val conf: SparkConf = new SparkConf()
.setMaster("local[2]")
.setAppName("stream")
val sc = new SparkContext(conf)
/**
* 2、创建SparkStreaming环境
*
* 指定batch的间隔时间
*/
val ssc = new StreamingContext(sc, Durations.seconds(5))
//设置checkpoint 用于保存之前的计算状态
//指定保存路径
ssc.checkpoint("data/checkpoint")
//读取数据
val linesDS: ReceiverInputDStream[String] = ssc.socketTextStream("master", 8888)
val wordDS: DStream[String] = linesDS.flatMap(line => line.split(","))
val kvDS: DStream[(String, Int)] = wordDS.map(word => (word, 1))
/**
* reduceByKey:按照key对value进行聚合,只在当前batch(当前的5秒间隔)中进行聚合
*
*/
/**
* updateStateByKey: 每一次计算使用当前batch的数据更新之前的计算结果,得到最新的结果
*
*/
/**
* seq: 每一个key当前Batch所有的value
* option: 前面这个key的计算结果(状态),之前可能没有结果
*/
val updateFun: (Seq[Int], Option[Int]) => Some[Int] = (seq: Seq[Int], option: Option[Int]) => {
//1、计算当前batch单词的数量
val currCount: Int = seq.sum
//前面计算的单词的数量
//前面可能没有值,所以给个默认值0
val lastCount: Int = option.getOrElse(0)
//返回最新的单词的数量
//返回 Option
Some(currCount + lastCount)
}
// updateFun -- 放在上面定义,为了好看
val countDS: DStream[(String, Int)] = kvDS.updateStateByKey(updateFun)
countDS.print()
ssc.start()
ssc.awaitTermination()
ssc.stop()
}
}
滑动窗口和滚动窗口
滑动窗口的使用及优化(包含滚动窗口)
实时统计单词的数量,每隔5秒统计最近15秒单词的数量
滚动窗口 -- 只指定一个窗口大小
滑动窗口的优化
因为滑动窗口会产生数据的交叉 -- 出现重复计算
package com.shujia.stream
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Durations, StreamingContext}
object Demo2Window {
def main(args: Array[String]): Unit = {
/**
* 1、创建SparkContext
*
*/
val conf: SparkConf = new SparkConf()
.setMaster("local[2]")
.setAppName("stream")
val sc = new SparkContext(conf)
/**
* 2、创建SparkStreaming环境
*
* 指定batch的间隔时间
*/
val ssc = new StreamingContext(sc, Durations.seconds(5))
//将需要被减去的值缓存
ssc.checkpoint("data/checkpoint")
val linesDS: ReceiverInputDStream[String] = ssc.socketTextStream("master", 8888)
val kvDS: DStream[(String, Int)] = linesDS.flatMap(_.split(",")).map((_, 1))
/**
* 每隔5秒统计最近15秒单词的数量
*
*/
//若只指定一个窗口大小的话就是滚动窗口
//没有优化的版本
/* val cuntDS: DStream[(String, Int)] = kvDS.reduceByKeyAndWindow(
(x: Int, y: Int) => x + y, //窗口内的聚合函数,如果函数后面还有参数的话,前面的参数需要加上类型
Durations.seconds(15), //窗口大小
Durations.seconds(5) //滑动时间
)*/
//优化版本
//加一个 减去多于数据的函数,但是为什么在代码中是这样的写的呢?
//下面画图解释
val cuntDS: DStream[(String, Int)] = kvDS.reduceByKeyAndWindow(
(x: Int, y: Int) => x + y, //窗口内的聚合函数
(i: Int, j: Int) => i - j, //减去多于数据的函数
Durations.seconds(15), //窗口大小
Durations.seconds(5) //滑动时间
)
//过滤掉为0的数据
val filterDS: DStream[(String, Int)] = cuntDS.filter(_._2 != 0)
filterDS.print()
ssc.start()
ssc.awaitTermination()
ssc.stop()
}
}
为什么在代码中是这样的写的呢?---- 图解