MapWithState 了解
UpdateStateBykey和MapWithState都是对DStream做批次累加操作,都可以将每个批次的结果进行累加计算,但是UpdateStateByKey是真正基于磁盘存储的,所有批次结果都会累加至磁盘,每次取值的时候也会直接访问磁盘,不管当前批次是否有值,都会获取之前批次结果数据,而MapWithState,虽然也是基于磁盘存储,但是 它合理使用内存,也就是缓存,MapWithState会首先将数据刷入内存,进行缓存操作,然后在把结果写入磁盘,而当批次没有数据的时候,不会访问磁盘,也不会打印当前数据,当有数据进来才会输出,并且首先访问内存,如果内存数据被清空再去访问磁盘数据,来做每次累加操作。
Transform
(类似Transforamtion)
可以在DStream中,使用RDD进行操作,然后返回的还是RDD,最后转换为DStream,一句话,在流中使用RDD
可以解决RDD与RDD之间的Join等,比如我们读取的字段文件,那么现在想把字典文件进行关联获取的数据流,需要将流转换为RDD处理。
案例:过滤广告黑名单
/**
* 案例:过滤广告黑名单
* 用户对网站上的广告进行点击,点击之后,要进行计费,点一次算一次的钱
* 但是,有的无良的商家恶意刷广告,那么我们需要一个黑名单系统
* 进行过滤黑名单用户
*/
object BlackListFilter {
def main(args: Array[String]): Unit = {
// 屏蔽log
Logger.getLogger("org").setLevel(Level.WARN)
val conf = new SparkConf().setAppName("filter").setMaster("local[2]")
val ssc = new StreamingContext(conf,Seconds(3))
// 设置黑名单
val blackList = Array(("zhangsan",false),("lisi",false))
// 创建RDD
val rdd = ssc.sparkContext.parallelize(blackList)
// val broad = ssc.sparkContext.broadcast(rdd.collect())
// 获取数据源
val inputDStream = ssc.socketTextStream("node4",9999)
// 数据流进行过滤
// inputDStream.transform(user=>{
// user.filter(!broad.value.toList.contains(_))
// }).print()
inputDStream.transform(user=>{
val tuples = user.map((_,1))
// 如果用join的话,那数据没有匹配上,也不会保存,所以用LeftJoin操作
val leftJoin: RDD[(String, (Int, Option[Boolean]))] = tuples.leftOuterJoin(rdd)
val tuple = leftJoin.filter(tuple =>{
tuple._2._2.getOrElse(true)
})
tuple.map(_._1)
}).print()
ssc.start()
ssc.awaitTermination()
}
}
foreachRDD
(类似Action)
输出方法,也是对RDD操作,但是无返回值,一般用于调用各种Connection连接使用,和Transform的区别在,一个是转换操作,一个是输出操作
滑动窗口(Window)
两个概念:
滑动时间:必须是每个批次提交的倍数 比如每批次提交5秒,那么滑动时间就必须是5的倍数
滑动距离:滑动距离是你批次的倍数,比如 每次滑动时间10秒,那么滑动距离可以是10,也可以是20或者15
滑动窗口的应用:
可以使用滑动窗口进行多批次统计,比如按照时间需求可以统计前几次批次结果集,例如:1分钟统计前10分钟的批次的结果集
例如:每五分钟显示前1小时的数据结果集
注意:每个窗口长度一定是批次的倍数
手动维护Offset(Zookeeper)重点
- 连接Zookeeper和Kafka
- 获取Offset(判断当前的Offset是否是第一次消费)
- 处理数据后,将Offset维护到ZK
好处:保证数据不丢失,同时保证每次数据消费的精准性,同时Zookeeper分布式的,可以分布式存储,保存offset安全,如果当前消费数据程序挂掉,计算结果没出来,那么Offset将不会被更新,下次继续消费当前数据
SparkStreaming的反压机制
设置最大接收速率 - 如果集群资源不够大,streaming 应用程序能够像接收到的那样快速处理数据,则可以通过设置 记录/秒 的最大速率限制。Direct Kafka 方法的 spark.streaming.kafka.maxRatePerPartition
,在Spark 1.5中,引入了一个称为背压的功能,无需设置此速率限制,因为Spark Streaming会自动计算速率限制,并在处理条件发生变化时动态调整速率限制。可以通过将 配置参数 spark.streaming.backpressure.enabled
设置为 true
来启用此 backpressure。
数据序列化
可以使用Spark中的内部序列化机制 Kryo序列化方式,进行序列化操作,相比较Java的序列化方式,速度要快10倍以上,序列化的大小要小于Java的序列化
保证消费一次性
dempotent updates(幂等更新):多次尝试总是写入相同的数据。例如,saveAs***Files
总是将相同的数据写入生成的文件。
Transactional updates(事务更新):所有更新都是事务性的,以便更新完全按原子进行。这样做的一个方法如下。