有状态算子、滑动窗口和滚动窗口

有状态算子

之前我们在运行 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()

  }
}

为什么在代码中是这样的写的呢?---- 图解

posted @ 2022-03-13 14:24  赤兔胭脂小吕布  阅读(149)  评论(0编辑  收藏  举报