Spark DStream转换算子 无状态化转换算子Transform 有状态转化算子UpdateStateByKey Window Operations

Spark DStream转换算子

DStream上的操作与RDD的类似,分为Transformations(转换)和Output Operations(输出)两种,此外转换操作中还有一些比较特殊的算子,如:updateStateByKey()、transform()以及各种Window相关的算子。

无状态化转换算子

无状态转化操作就是把简单的RDD转化操作应用到每个批次上,也就是转化DStream中的每一个RDD,部分无状态转化操作列在了下表中

在这里插入图片描述

需要记住的是,尽管这些函数看起来像作用在整个流上一样,但事实上每个DStream在内部是由许多RDD(批次)组成,且无状态转化操作是分别应用到每个RDD上的。例如:reduceByKey()会归约每个时间区间中的数据,但不会归约不同区间之间的数据。

这里特别介绍无状态转换算子Transform

Transform允许DStream上执行任意的RDD-to-RDD函数。即使这些函数并没有在DStream的API中暴露出来,通过该函数可以方便的扩展Spark API。该函数每一批次调度一次。其实也就是对DStream中的RDD应用转换。

package com.xcu.bigdata.spark.streaming

import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}

/**
 * @Package : com.xcu.bigdata.spark.streaming
 * @Author : 
 * @Date : 2020 10月 星期一
 * @Desc : 使用transform算子将DS转换为RDD
 */
object SparkStreaming08_Transform {
  def main(args: Array[String]): Unit = {
    //创建配置文件对象
    //注意Streaming程序执行至少需要2个线程,所以不能设置为local
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming08_Transform").setMaster("local[*]")
    //创建SparkStreaming程序执行入口
    //指定采集周期为3s
    val ssc = new StreamingContext(conf, Seconds(3))
    //从指定端口读取数据
    val socketDS: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 8888)
    //将DS转换为RDD进行操作
    val resRDD: DStream[(String, Int)] = socketDS.transform(
      rdd => {
        val flatMapRDD: RDD[String] = rdd.flatMap(_.split(" "))
        val mapRDD: RDD[(String, Int)] = flatMapRDD.map((_, 1))
        val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_ + _)
        reduceRDD.sortByKey()
      }
    )
    resRDD.print()
    //启动采集器
    ssc.start()
    //等待采集结束之后,终止程序
    ssc.awaitTermination()
  }
}

有状态转化算子

这里特别介绍UpdateStateByKey算子

有时,我们需要在DStream中跨批次维护状态(例如流计算中累加wordcount)。针对这种情况,updateStateByKey()为我们提供了对一个状态变量的访问,用于键值对形式的DStream。给定一个由(键,事件)对构成的 DStream,并传递一个指定如何根据新的事件更新每个键对应状态的函数,它可以构建出一个新的 DStream,其内部数据为(键,状态) 对。

UpdateStateByKey() 的结果会是一个新的DStream,其内部的RDD 序列是由每个时间区间对应的(键,状态)对组成的。

package com.xcu.bigdata.spark.streaming

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
 * @Package : com.xcu.bigdata.spark.streaming
 * @Author : 
 * @Date : 2020 11月 星期五
 * @Desc : 有状态化的转化算子UpdateStateByKey
 */
object SparkStreaming09_UpdateStateByKey {
  def main(args: Array[String]): Unit = {
    //创建配置文件对象
    //注意Streaming程序执行至少需要2个线程,所以不能设置为local
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming09_UpdateStateByKey").setMaster("local[*]")
    //创建SparkStreaming程序执行入口
    //指定采集周期为3s
    val ssc = new StreamingContext(conf, Seconds(3))
    //设置检查点路径,状态保存在checkpoint中
    ssc.checkpoint("./cp")

    //从指定端口读取数据
    val socketDS: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 8888)
    //对数据进行扁平化
    val flatMapDS: DStream[String] = socketDS.flatMap(_.split(" "))
    //结构转换
    val mapDS: DStream[(String, Int)] = flatMapDS.map((_, 1))
    val stateDS: DStream[(String, Int)] = mapDS.updateStateByKey(
      //第一个参数:表示相同的key对应的value组成的数据集合
      //第二个参数:第二个参数Option,相同key对应的状态(即上一个采集周期的结果)
      (seq: Seq[Int], state: Option[Int]) => {
        //对当前key对应的value进行求和 seq.sum
        //state.getOrElse(0) 获取缓冲区的数据 如果第一次,那么缓存区放一个0
        Option(seq.sum + state.getOrElse(0))
      }
    )
    stateDS.print()
    //启动采集器
    ssc.start()
    //等待采集结束之后,终止程序
    ssc.awaitTermination()
  }
}
这里特别介绍Window Operations(窗口操作)

Spark Streaming也提供了窗口计算, 允许执行转换操作作用在一个窗口内的数据。 默认情况下, 计算只对一个时间段内的RDD进行, 有了窗口之后, 可以把计算应用到一个指定的窗口内的所有 RDD 上。一个窗口可以包含多个时间段,基于窗口的操作会在一个比StreamingContext的批次间隔更长的时间范围内,通过整合多个批次的结果,计算出整个窗口的结果。所有基于窗口的操作都需要两个参数,分别为窗口时长以及滑动步长。

窗口时长:计算内容的时间范围

滑动步长:隔多久触发一次计算

注意:这两者都必须为采集周期的整数倍

package com.xcu.bigdata.spark.streaming

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

/**
 * @Package : com.xcu.bigdata.spark.streaming
 * @Author : 
 * @Date : 2020 11月 星期五
 * @Desc : 有状态的转换算子window的使用
 */
object SparkStreaming10_Window {
  def main(args: Array[String]): Unit = {
    //创建配置文件对象
    //注意Streaming程序执行至少需要2个线程,所以不能设置为local
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming10_Window").setMaster("local[*]")
    //创建SparkStreaming程序执行入口
    //指定采集周期为3s
    val ssc = new StreamingContext(conf, Seconds(3))
    //从指定端口读取数据
    val socketDS: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 8888)
    //设置窗口的大小及滑动步长 以上两个值都应该是采集周期的整数倍
    val windowDS: DStream[String] = socketDS.window(Seconds(6), Seconds(3))
    //业务逻辑
    val resDS: DStream[(String, Int)] = windowDS
      .flatMap(_.split(" "))
      .map((_, 1))
      .reduceByKey(_ + _)
    //输出
    resDS.print()
    //启动采集器
    ssc.start()
    //等待采集结束之后,终止程序
    ssc.awaitTermination()
  }

}
关于Window的操作还有如下方法
1)window(windowLength, slideInterval)

基于对源DStream窗化的批次进行计算返回一个新的Dstream

2)countByWindow(windowLength, slideInterval)

返回一个滑动窗口计数流中的元素个数

3)countByValueAndWindow()

返回的DStream则包含窗口中每个值的个数

4)reduceByWindow(func, windowLength, slideInterval)

通过使用自定义函数整合滑动区间流元素来创建一个新的单元素流

5)reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks])

当在一个(K,V)对的DStream上调用此函数,会返回一个新(K,V)对的DStream,此处通过对滑动窗口中批次数据使用reduce函数来整合每个key的value值

6)reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks])

这个函数是上述函数的变化版本,每个窗口的reduce值都是通过用前一个窗的reduce值来递增计算。通过reduce进入到滑动窗口数据并”反向reduce”离开窗口的旧数据来实现这个操作。如果把3秒的时间窗口当成一个池塘,池塘每一秒都会有鱼游进或者游出,那么第一个函数表示每由进来一条鱼,就在该类鱼的数量上累加。而第二个函数是,每由出去一条鱼,就将该鱼的总数减去一。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页