比reduceByWindow更高效的reduceByKeyAndWindow()的实现版本

reduceByKeyAndWindow这个算子也是lazy的,它用来计算一个区间里面的数据,如下图:

比reduceByWindow更高效的reduceByKeyAndWindow()的实现版本_第1张图片

实现代码如下:

import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.ReceiverInputDStream
import org.apache.spark.streaming.kafka.KafkaUtils

object SparkReduceByKeyAndWindow {
  def main(args: Array[String]): Unit = {

    //由于日志信息较多,只打印错误日志信息
    Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)

    val conf = new SparkConf().setAppName("dstream").setMaster("local[*]")
    //把批处理时间设置为5s一次
    val ssc = new StreamingContext(conf,Seconds(5))
    //使用updateStateByKey前需要设置checkpoint,将数据进行持久化保存,不然每次执行都是新的,不会与历史数据进行关联
        ssc.checkpoint("f:/spark_out")
    //将数据保存在hdfs中
//    ssc.checkpoint("hdfs://192.168.200.10:9000/spark_out")
    //与kafka做集成,使用KafkaUtils类进行参数配置
    val(zkQuorum,groupId,topics)=("192.168.200.10:2181","kafka_group",Map("sparkTokafka"->1))
    val value: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc,zkQuorum,groupId,topics)
    //对滑动窗口中新的时间间隔内数据增加聚合并移去最早的与新增数据量的时间间隔内的数据统计量
    //Seconds(10)表示窗口的宽度   Seconds(15)表示多久滑动一次(滑动的时间长度)
    //综上所示:每5s进行批处理一次,窗口时间为15s范围内,每10s进行滑动一次
    //批处理时间<=滑动时间<=窗口时间,如果三个时间相等,表示默认处理单个的time节点
    value.flatMap(_._2.split(" ")).map((_,1)).reduceByKeyAndWindow(((x:Int,y:Int)=>x+y),Seconds(15),Seconds(10)).print()
    //启动数据接收
    ssc.start()
    //来等待计算完成
    ssc.awaitTermination()
  }
}
总的来说:SparkStreaming提供这个方法主要是出于效率考虑。 比如说我要每10秒计算一下前15秒的内容,(每个batch 5秒), 可以想象每十秒计算出来的结果和前一次计算的结果其实中间有5秒的时间值是重复的。 
那么就是通过如下步骤 
1. 存储上一个window的reduce值 
2.计算出上一个window的begin 时间到 重复段的开始时间的reduce 值 =》 oldRDD 
3.重复时间段的值结束时间到当前window的结束时间的值 =》 newRDD 
4.重复时间段的值等于上一个window的值减去oldRDD 

这样就不需要去计算每个batch的值, 只需加加减减就能得到新的reduce出来的值。 


从代码上面来看, 入口为: 

reduceByKeyAndWindow(_+_, _-_, Duration, Duration) 

先计算oldRDD 和newRDD 

//currentWindow  就是以当前时间回退一个window的时间再向前一个batch 到当前时间的窗口 代码里面有一个图很有用: 
我们要计算的new rdd就是15秒-25秒期间的值, oldRDD就是0秒到10秒的值, previous window的值是1秒 - 15秒的值 

然后最终结果是 重复区间(previous window的值 - oldRDD的值) =》 也就是中间重复部分, 再加上newRDD的值, 这样的话得到的结果就是10秒到25秒这个时间区间的值 

// 0秒                  10秒     15秒                25秒  
//  _____________________________  
// |  previous window   _________|___________________  
// |___________________|       current window        |  --------------> Time  
//                     |_____________________________|  
//  
// |________ _________|          |________ _________|  
//          |                             |  
//          V                             V  
//       old RDDs                     new RDDs  
//  

你可能感兴趣的:(Spark)