Spark复习 Day04:SparkStreaming

Spark复习 Day04:SparkStreaming

1. SparkStreaming版的WordCount
---------------------------------
     @Test
      def TestStreaming(): Unit ={
        val conf = new SparkConf().setAppName("sc").setMaster("local")
        val streamingContext = new StreamingContext(conf, Seconds(3))
        val scDStream: ReceiverInputDStream[String] = streamingContext.socketTextStream("localhost", 9988)
        val res: DStream[(String, Int)] = scDStream.flatMap(_.split("\\s+")).map((_,1)).reduceByKey(_ + _)
        res.print()
        // 开启streaming
        streamingContext.start()
        // 等待采集器结束而结束
        streamingContext.awaitTermination()
      }


2. SparkStreaming 数据源
--------------------------------------
    - 文件数据源
        1. streamingContext.textFileStream(dir)
        2. 监控一个目录,产生新文件就读取
        3. 文件需要是相同的数据格式
        4. 文件一旦进入目录,就不能再次修改,修改也不会重新读取数据

    - 在内存中序列化数据源
         @Test
          def TestQueueRDD(): Unit ={
            val conf = new SparkConf().setMaster("local").setAppName("sc")
            val streamingContext = new StreamingContext(conf,Seconds(3))
            val queue: mutable.Queue[RDD[Int]] = mutable.Queue[RDD[Int]]()
            val stream: InputDStream[Int] = streamingContext.queueStream(queue,oneAtATime = false)
            val res: DStream[(Int, Int)] = stream.map((_,1)).reduceByKey(_ + _)
            res.print()
            streamingContext.start()
            for(i <- 1 to 1000){
              val rdd = streamingContext.sparkContext.makeRDD(1 to i * 2,10)
              queue.enqueue(rdd)
              Thread.sleep(2000)
            }
            streamingContext.awaitTermination()

    - 自定义数据源
        class MyReceiver extends Receiver[String](StorageLevel.MEMORY_AND_DISK) {

          override def onStart(): Unit = {

          }

          override def onStop(): Unit = {

          }
        }

    - kafka数据源
        1. pom
         
            
                org.apache.spark
                spark-streaming-kafka-0-8_2.11
                ${spark.version}
            

            
            
                org.apache.kafka
                kafka-clients
                ${kafka.version}
            

        2. KafkaUtils.scala
            @Test
            def TestKafka(): Unit ={
              val conf = new SparkConf().setMaster("local").setAppName("sc")
              val streamingContext = new StreamingContext(conf,Seconds(3))
              val kafkaStream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(
                ssc = streamingContext,
                zkQuorum = "hadoop01:2181,hadoop02:2181,hadoop02:2181",
                groupId = "groupId",
                topics = Map("topic1" -> 3)
              )
              val res: DStream[String] = kafkaStream.flatMap(t => t._2.split("\\s+"))
              res.print()
              streamingContext.start()
              streamingContext.awaitTermination()
            }


3. DStream 的转换
---------------------------------------------
    - 无状态操作
        1. 无状态转化就是把简单的计算逻辑应用到批次的每个RDD上。只对当前批次的RDD有影响,对以前批次的RDD毫无关联
        2. 主要有:
            map
            flatMap
            filter
            repartition
            reduceByKey : 只会计算当前批次RDD的数据,不会跨批次
            groupByKey
            ...

    - 有状态操作
        1. 操作会保存以前批次的状态[计算结果],与当前批次进行交互计算
        2. 可以保存到内存或者文件系统
        3. 主要有:
            - UpdateStateByKey
            - Window Operations


4. 有状态算子 UpdateStateByKey
----------------------------------------------------
    - 用于跨批次维护状态,例如流计算中累加单词统计
    - UpdateStateByKey的结果是一个新的DStream,其内部的RDD序列是由每个批次计算结果[k,v]组成的
    - 使用:
        1. 定义状态,状态可以是任意的数据类型、用来保存以前批次的计算结果
        2. 定义状态更新函数: 以前的状态 + 输入流的数据 => 新的状态
    - UpdateStateByKey需要对检查点目录进行配置,算法内部需要进行检查点保存计算的结果[状态]
    - 例:
      @Test
      def testKafka(): Unit ={
        val conf = new SparkConf().setMaster("local").setAppName("sc")
        val streamingContext = new StreamingContext(conf,Seconds(3))
        val kafkaStream: DStream[(String, Int)] = KafkaUtils.createStream(
          ssc = streamingContext,
          zkQuorum = "hadoop01:2181,hadoop02:2181,hadoop02:2181",
          groupId = "groupId",
          topics = Map("topic1" -> 3)
        ).map(_._2).flatMap(_.split("\\s+")).map((_,1))
        // updateStateByKey使用前,需要指定检查点
        streamingContext.sparkContext.setCheckpointDir("cp")
        // 简单的统计kafka单词出现的次数
        // updateStateByKey的ByKey说明了此算子肯定会先根据key进行分组,相同的key进入一组,所以,算子里面只操作value即可,然后更新key的value
        // 我缓存的状态 其实就是一个Option[Int]的值,就是updateFunc方法的返回值
        // 首先,第一次进来,seq[Int]里面是第一批进来,单词==key 的单词的出现的次数 seq[1,1,1,1] = 本批次单词key出现了4次
        // 然后去buffer里面取缓存的状态,因为buffer里面是没有值的,所以取出默认值0
        // 然后执行你定义的逻辑 sum(seq) + buffer ==> 结果为4 ==> 截止至当前批次,单词统计为4,并且更新到缓存buffer里面
        // 下一个批次key单词出现了seq[1,1] 传入,取出buffer里面的4,执行你的逻辑,计算结果 =6, 更新缓冲区
        // 依次循环,去更新缓冲区
        // 然后每隔 new StreamingContext(conf,Seconds(3)) 限定的时间,去打印一下数据
        val res: DStream[(String, Int)] = kafkaStream.updateStateByKey({
          // updateFunc
          case (seq: Seq[Int], buffer: Option[Any]) =>
            // seq中缓存的都是以前的单词的单词数量,怎么操作你说了算
            Option(buffer.getOrElse(0) + seq.sum)
        })
        res.print()
        streamingContext.start()
        streamingContext.awaitTermination()
      }


5. 有状态算子 Window Operations
-----------------------------------------------------------
    - Window Operations 允许你设置一个窗口的大小和滑动的间隔,来动态计算和获取缓冲区的状态
    - 首先,你的streamingContext有一个采集周期例如3s,你的窗口可以设置成10分钟,这样就会将最近200次采集的结果整合到一起
    - 然后,设置成每1分钟滑动一次,那么你的窗口就是一分钟统计一次实时的10分钟内的数据结果
    - 常用操作:
        DStream.window(duration, slider)
        DStream.countByWindow(duration, slider)
        DStream.reduceByWindow(func, duration, slider)
        DStream.reduceByKeyAndWindow(func, duration, slider, [numTasks])
    - 例:
      @Test
      def testWindowOperations(): Unit ={
        val conf = new SparkConf().setMaster("local").setAppName("sc")
        val streamingContext = new StreamingContext(conf,Seconds(3))
        val kafkaStream: DStream[(String, String)] = KafkaUtils.createStream(
          ssc = streamingContext,
          zkQuorum = "hadoop01:2181,hadoop02:2181,hadoop02:2181",
          groupId = "groupId",
          topics = Map("topic1" -> 3)
        )
        // 窗口大小和滑动都是采集周期的整数倍
        // 这样就每1分钟,统计最近10分钟的数据,然后计算一个结果
        val windowDStream: DStream[(String, String)] = kafkaStream.window(Minutes(10), Minutes(1))
        val res: DStream[(String, Int)] = windowDStream.map(_._2).flatMap(_.split("\\s+")).map((_,1)).reduceByKey(_ + _)
        res.print()
        streamingContext.start()
        streamingContext.awaitTermination()
      }


6. Transform 算子
--------------------------------------------------
      // 转换,就是封装一些RDD的操作,作为一个transform
        // 但是,如果涉及到需要动态更新的逻辑,这个就起到很大的作用了
        kafkaStream.transform(x => {
          // TODO 此位置写的代码,都是重复执行的,同步于streamingContext的采集频率
          // 这里可以处理你的动态更新逻辑
          // 可以放你的需要每批次更改的变量等等
          // 比如黑名单机制,你每次读取一个批次的数据,都要实时更新,读取最新的黑名单机制
          x.map(cc => cc._1)
        })


7. foreachRDD
-------------------------------------------------
    - 一个streamingContext的采集周期会形成一个RDD
    - 一个DStream 会有一个或者多个RDD
    - DStream.foreachRDD(rdd => ....)  // 能够取到每一个RDD

你可能感兴趣的:(大数据,Spark)