SparkStreaming foreachRDD&transform&updateStateByKey&renduceByKeyAndWindow算子

各种算子请查看官网:SparkStreaming
下面列举常用几个示例:

1. foreachRDD

  • output operation 算子,必须对抽取出来的 RDD 执行 action 类算子
    代码才能执行。
import org.apache.spark.SparkConf
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Durations, StreamingContext}

object ForeachRDDTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setMaster("local[2]")
    conf.setAppName("test")
    val ssc = new StreamingContext(conf, Durations.seconds(5))

    val lines = ssc.socketTextStream("node4", 9999)
    val result: DStream[(String, Int)] = lines.flatMap(line => {
      //      println("************"+line)
      line.split(" ")
    }).map(word => {
      new Tuple2(word, 1)
    }).reduceByKey((v1, v2) => {
      v1 + v2
    })

    /**
     * foreachRDD :
     * 1).foreachRDD 可以获取DStream中的RDD,可以对RDD使用RDD的transformation类算子进行转换,
     * 但是一定要对RDD进行Actioin算子触发,不然DStream的逻辑也不会执行。
     * 2).foreachRDD算子内的代码,获取的RDD算子外的代码是在Driver端执行的,每隔batchInterval执行一次。可以利用这个特点动态的改变广播变量
     */
    result.foreachRDD(rdd => {
      println("**** Driver ****")
      val bc: Broadcast[String] = rdd.sparkContext.broadcast("读取数据库mysql中的数据")
      val map: RDD[String] = rdd.map(tp => {
        println("===== Executor-map =====" + tp)
        val value: String = bc.value
        tp._1 + "-" + tp._2
      })
      val end = map.filter(s => {
        true
      })
      end.foreach(println) //进行Actioin算子触发,确保RDD中的逻辑执行
    })
    ssc.start()
    ssc.awaitTermination()
  }
}

2. transform

  • transformation 类算子
  • 可以通过 transform 算子,对 Dstream 做 RDD 到 RDD 的任意操
    作。

代码示例:

import org.apache.spark.{SparkConf}
import org.apache.spark.streaming.{Durations, StreamingContext}


/**
 * transform :
 * 1).DStream的Transformation算子,可以获取DStream中的RDD,对RDD进行RDD的Transformation类算子转换,也可以使用Action算子。
 * 但是一定最后需要返回一个RDD,将返回的RDD会包装到一个DStream中。
 * 2).与foreachRDD类似,算子内的代码获取的RDD算子外的代码是在Driver端执行的,可以通过这个算子来动态的改变广播变量的值
 */
object TestTransform {
  def main(args: Array[String]): Unit = {
    val context: StreamingContext = new StreamingContext(new SparkConf()
      .setMaster("local[2]")
      .setAppName("TestTransform"), Durations.seconds(5))

    val lines = context.socketTextStream("node4", 9999)

    lines.transform(rdd => {
      /**
       * 与foreachRDD类似,算子内的代码获取的RDD算子外的代码是在Driver端执行的,
       * 可以通过这个算子来动态的改变广播变量的值
       */
      println("***** Driver ******")
      rdd.filter(r => !r.contains("zhangsan"))
    }).print()
    context.start()
    context.awaitTermination()
  }
}

3. updateStateByKey算子

  • transformation 算子
  • updateStateByKey 作用:
  1. 为 SparkStreaming 中每一个 Key 维护一份 state 状态,state
    类型可以是任意类型的,可以是一个自定义的对象,更新函数也
    可以是自定义的。
  2. 通过更新函数对该 key 的状态不断更新,对于每个新的 batch 而
    言,SparkStreaming 会在使用 updateStateByKey 的时候为已
    经存在的 key 进行 state 的状态更新。
  • 使用到 updateStateByKey 要开启 checkpoint 机制和功能。
  • 多久会将内存中的数据写入到磁盘一份?
    如果 batchInterval 设置的时间小于 10 秒,那么 10 秒写入磁盘一
    份。如果 batchInterval 设置的时间大于 10 秒,那么就会
    batchInterval 时间间隔写入磁盘一份。
    代码示例:
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Durations, StreamingContext}

/**
 * updataeStateByKey :
 * 1).可以更新key的状态,统计自从SparkStreaming 启动以来所有key的状态值
 * 2).需要设置checkpoint 来保存之前所有key对应的value状态值
 * 默认状态是在内存中的,必须设置checkpoint保存状态,多久将内存中的装填向checkpoint持久化一次?
 * 如果batchInterval 小于10s ,那就10s保存一次,如果batchInterval 大于10s 就batchInterval 保存一次,为了避免频繁的磁盘IO。
 */
object UpdateStateByKey {
  def main(args: Array[String]): Unit = {
    val context: StreamingContext = new StreamingContext(new SparkConf()
      .setMaster("local[2]")
      .setAppName("UpdateStateByKey"), Durations.seconds(5))
    context.sparkContext.setLogLevel("Error")
    context.checkpoint("T:/code/spark_scala/data/sparkstreaming/ck")

    val lines: ReceiverInputDStream[String] = context.socketTextStream("node4", 9999)
    val words: DStream[String] = lines.flatMap(line => {
      line.split(" ")
    })
    val pairWords: DStream[(String, Int)] = words.map(word => {
      (word, 1)
    })

    /**
     * updateStateByKey : 按照key 分组,针对每个分组内的数据进行处理
     * seq: 当前组内的value组成的集合
     * option : 当前批次之前批次对应某个key对应的值
     */
    pairWords.updateStateByKey((seq: Seq[Int], option: Option[Int]) => {
      var value = option.getOrElse(0)
      for (currentValue <- seq) {
        value += currentValue
      }
      Option(value)
    }).print()
    context.start()
    context.awaitTermination()
  }
}

结果:

-------------------------------------------
Time: 1604658690000 ms
-------------------------------------------
(hello,2) // 之前输入的key状态会被保留。再输入时value+现在组内value的数量
(spark,1)
(人在囧途,1)

4. SparkStreaming renduceByKeyAndWindow算子

4.1

示意图:
在这里插入图片描述
官网示意图;
在这里插入图片描述

示例代码:

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Durations, StreamingContext}

/**
 * 需求:
 * 每隔一段时长,统计最近一段时间数据。
 * reduceByKeyAndWindow: 每隔一段时长统计最近一段时间的内的数据。
 * 注意:
 * 窗口长度 - window length - wl
 * 滑动间隔 - slding interval - si
 * 窗口长度和滑动间隔必须是batchInterval的整数倍
 */
object ReduceByKeyAndWindow1 {
  def main(args: Array[String]): Unit = {
    val context: StreamingContext = new StreamingContext(new SparkConf()
      .setMaster("local[2]")
      .setAppName("ReduceByKeyAndWindow1"), Durations.seconds(5))
    context.sparkContext.setLogLevel("Error")
    val lines = context.socketTextStream("node4", 9999)
    lines.flatMap(line => line.split(" "))
      .map((_, 1))
      .reduceByKeyAndWindow((v1: Int, v2: Int) => {
        v1 + v2
        // 每隔 滑动间隔  统计最近 窗口长度内的数据,按照指定逻辑计算。
      }, Durations.seconds(15), Durations.seconds(5))
      .print()
    context.start()
    context.awaitTermination()
  }
}

在服务器上输入:

[root@node4 ~]# nc -lk 9999
自强不息

结果显示3个batchInterval即一个窗口长度的时间

Time: 1604660660000 ms
-------------------------------------------
(自强不息,1)

-------------------------------------------
Time: 1604660665000 ms
-------------------------------------------
(自强不息,1)

-------------------------------------------
Time: 1604660670000 ms
-------------------------------------------
(自强不息,1)

-------------------------------------------
Time: 1604660675000 ms
-------------------------------------------

4.2 SparkStreaming renduceByKeyAndWindow 优化处理方式原理

官网截图:
在这里插入图片描述

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Durations, StreamingContext}

/**
 * reduceByKeyAndWindow : 优化方式处理数据,在集群处理数据能力不足时使用
 * 1).reduceByKeyAndWindow(fun1,fun2,窗口长度,滑动间隔)
 * 2).优化方式需要设置checkpoint 保存状态
 * 3).进来k对应的value值减去出去key对应的value值,保存其状态
 */
object ReduceByKeyAndWindow2 {
  def main(args: Array[String]): Unit = {
    val context: StreamingContext = new StreamingContext(new SparkConf()
      .setMaster("local[2]")
      .setAppName("ReduceByKeyAndWindow2"), Durations.seconds(5))
    context.sparkContext.setLogLevel("Error")
    context.checkpoint("T:/code/spark_scala/data/sparkstreaming/ck")
    val lines = context.socketTextStream("node4", 9999)
    lines.flatMap(line => line.split(" "))
      .map((_, 1))
      .reduceByKeyAndWindow((v1: Int, v2: Int) => {
        v1 + v2
        // 每隔 滑动间隔  统计最近 窗口长度内的数据,按照指定逻辑计算。
      }, (v1: Int, v2: Int) => {
        v1 - v2
      }, Durations.seconds(15), Durations.seconds(5))
      .print()
    context.start()
    context.awaitTermination()
  }
}

4.3 window 窗口函数 ,可以每隔一段时间,将最近的窗口长度内的数据组成一个DStream处理。

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

/**
  * window 窗口函数 ,可以每隔一段时间,将最近的窗口长度内的数据组成一个DStream处理。
  */
object WindowTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setMaster("local[2]")
    conf.setAppName("test")
    val ssc = new StreamingContext(conf,Durations.seconds(5))
    ssc.sparkContext.setLogLevel("Error")
    val lines = ssc.socketTextStream("mynode5",9999)
    val blackList = List[String]("zhangsan","lisi","wangwu")
    //window 窗口函数
    val windowDs: DStream[String] = lines.window(Durations.seconds(15),Durations.seconds(5))
    //输入数据  hello xxx
    val result = windowDs.filter(one => {
      !blackList.contains(one.split(" ")(1))
    })
    result.print()
    ssc.start()
    ssc.awaitTermination()
  }
}

5.textFileStream 算子

可以监控目录下的增量文件数据,增加的数据格式必须是text格式,并且目录下已经存在的数据不能监控到,已经存在的数据追加数据也不能被监控到。
只能监控原子产生在目录下的数据。

6. saveAsTextFile算子

saveAsTextFile : 将数据结果保存到目录中,可以指定前缀后缀,多级目录可以指定在前缀中。

代码示例:

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

/**
  *  textFileStream :
  *    可以监控目录下的增量文件数据,增加的数据格式必须是text格式,并且目录下已经存在的数据不能监控到,已经存在的数据追加数据也不能被监控到。
  *    只能监控原子产生在目录下的数据。
  *
  *  saveAsTextFile : 将数据结果保存到目录中,可以指定前缀后缀,多级目录可以指定在前缀中。
  *
  */
object StreamingMonitorDIR {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setMaster("local")
    conf.setAppName("test")
    val ssc = new StreamingContext(conf,Durations.seconds(5))
    val lines: DStream[String] = ssc.textFileStream("./data/streamingCopyFile")
    lines.flatMap(line=>{line.split(" ")})
      .map(word=>{(word,1)})
      .reduceByKey((v1,v2)=>{v1+v2})
        .saveAsTextFiles("./data/streamingresult/aaa","bbb")
    ssc.start()
    ssc.awaitTermination()
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值