SparkStreaming_04

1.背压机制

背压机制(即Spark Streaming Backpressure): 根据JobScheduler反馈作业的执行信息来动态调整Receiver数据接收率

2.DStream

在内部实现上,DStream是一系列连续的RDD来表示。每个RDD含有一段时间间隔内的数据。

image-20211124201245281

image-20211124201353902

ReceiverAPI:需要一个专门的Executor去接收数据,然后发送给其他的Executor做计算。存在的问题,接收数据的Executor和计算的Executor速度会有所不同

DirectAPI:是由计算的Executor来主动消费Kafka的数据,速度由自身控制。

无状态转化操作就是把简单的RDD转化操作应用到每个批次上

3.Spark Streamming 有哪几种方式消费Kafka中的数据?

Receiver:

  1. 高阶API,获取的数据存储在Executor内存中
  2. Receiver是单点读数据,如果挂掉,程序不能运行
  3. 避免数据丢失启用预写入日志机制

Direct:

  1. 低阶API,每隔一段时间,去kafka读取一批数据
  2. 简化并行度,rdd的分区数量=topic的分区数量
  3. 不需要开启预写入机制,通过Kafka的副本进行恢复

对比:

  1. 基于receiver的方式高阶AP,是通过ZooKeeper保存消费的offset,Spark与ZooKeeper之间可能是不同步的,无法保证数据被精确处理一次
  2. 基于direct的方式,自己负责追踪消费的offset,并保存在checkpoint中,保证数据精确消费一次
		<dependency>
          <groupId>org.apache.spark</groupId>
          <artifactId>spark-core_2.11</artifactId>
          <version>2.1.1</version>
        </dependency>
        <dependency>
          <groupId>org.apache.spark</groupId>
          <artifactId>spark-sql_2.11</artifactId>
          <version>2.1.1</version>
        </dependency>
        <dependency>
          <groupId>org.apache.spark</groupId>
          <artifactId>spark-streaming_2.11</artifactId>
          <version>2.1.1</version>
        </dependency>
        <dependency>
          <groupId>org.apache.spark</groupId>
          <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
          <version>2.3.1</version>
        </dependency>

3.1操作

  1. 启动hadoop集群

  2. 启动zookeeper

    bin/zkServer.sh start

  3. 启动kafka

    开启
    nohup bin/kafka-server-start.sh config/server.properties &
    nohup bin/kafka-server-start.sh config/server.properties &
    nohup bin/kafka-server-start.sh config/server.properties &

    关闭
    bin/kafka-server-stop.sh stop

    创建主题
    bin/kafka-topics.sh --zookeeper hadoop101:2181 --create --replication-factor 3 --partitions 1 --topic first

    删除topic
    bin/kafka-topics.sh --zookeeper hadoop101:2181 --delete --topic first

    创建主题
    bin/kafka-topics.sh --zookeeper hadoop101:2181 --create --replication-factor 3 --partitions 3 --topic second

    生产消息
    bin/kafka-console-producer.sh --broker-list hadoop101:9092 --topic second

    消费消息
    bin/kafka-console-consumer.sh --zookeeper hadoop101:2181 --from-beginning --topic second

3.2 direct代码

import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.SparkConf
import org.apache.spark.streaming.kafka010.{KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe

object KafkaDirectorDemo {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("demo1").setMaster("local[*]")
    val ssc = new StreamingContext(conf, Seconds(5))
    //设置数据检查点进行累计计算
    ssc.checkpoint(".")
    //创建读取kafka的参数
    val kafkaParams = Map[String, Object](
      "bootstrap.servers" -> "hadoop101:9092,hadoop102:9092,hadoop103:9092", //用于初始化链接到集群的地址
      "key.deserializer" -> classOf[StringDeserializer], //key序列化
      "value.deserializer" -> classOf[StringDeserializer], //value序列化
      "group.id" -> "test-consumer-group", //用于标识这个消费者属于哪个消费团体
      "auto.offset.reset" -> "latest", //偏移量 latest自动重置偏移量为最新的偏移量
      "enable.auto.commit" -> (false: java.lang.Boolean) //如果是true,则这个消费者的偏移量会在后台自动提交
    )
    //kafka 设置kafka读取topic
    val topics = Array("third")

    //LocationStrategies.PreferConsistent任务尽量均匀分布在各个executor节点
    // 创建DStream,返回接收到的输入数据
    // LocationStrategies:根据给定的主题和集群地址创建consumer
    // LocationStrategies.PreferConsistent:持续的在所有Executor之间分配分区
    // ConsumerStrategies:选择如何在Driver和Executor上创建和配置Kafka Consumer
    // ConsumerStrategies.Subscribe:订阅一系列主题
    //createDirectStream[String,String] 指定消费kafka的message的key/value的类型
    //ConsumerStrategies.Subscribe[String,String]指定key/value的类型
    val dStream = KafkaUtils.createDirectStream(ssc, LocationStrategies.PreferConsistent, Subscribe[String, String](topics, kafkaParams))
    val rdd01 = dStream.map(x => (x.key(), x.value()))
    val words = rdd01.map(x => x._2).flatMap(_.split(" ")).map(x => (x, 1)).reduceByKey(_ + _)
    rdd01.print()
    words.print()
    ssc.start()
    ssc.awaitTermination()
  }
}

4.DStream算子

NetCat安装链接

nc -lp 9999 开启端口测试

4.1count,reduce,foreachRdd

object Demo1 {
  def main(args: Array[String]): Unit = {
    //注意运输模式为local的时候必须至少设置2个core, local[2]
    //接收socket的数据,需要一个对应的接收器,这个接收器就会使用cpu核
    //处理数据就需要一个执行器,这个执行器也会使用cpu核
    val conf = new SparkConf().setAppName("wordcount").setMaster("local[2]")
    //需要两个参数 第一个是 SparkConf  第二个是时间间隔,每个批次的数据的时间范围的长度
    val ssc = new StreamingContext(conf, Seconds(5)) //每5秒是一个时间批次
    //接收一个socket端口的数据
    //两个参数 第一参数:hostname或者是IP地址 第二个参数:端口号
    val lines = ssc.socketTextStream("hadoop101", 9999);
    //    val word = lines.flatMap(_.split(" ")).map(word=>(word,1)).reduceByKey(_+_)
    //    lines.print()
    //    word.print()

    //同一个批次中RDD的个数,和RDD内容无关
    //    val ds01 = lines.count()
    //    ds01.print()

    //同一个批次中的RDD做拼接
    //    val ds02 = lines.reduce(_+_)
    //    val ds02 = lines.reduce((a,b)=>(a+"~"+b))
    //    ds02.print()
    lines.foreachRDD { //foreachRDD内的代码在Driver端执行
      (rdd, time) => {
        //RDD上的算子
        val rdd01 = rdd.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
        rdd01.foreach(t => println(t, time))
      }
    }
    ssc.start()
    ssc.awaitTermination()
  }
}

4.2updateStateByKey,reduceByKeyAndWindow

  import org.apache.spark.SparkConf
  import org.apache.spark.streaming.{Seconds, StreamingContext}
  
  object Demo2 {
    def main(args: Array[String]): Unit = {
      //注意运输模式为local的时候必须至少设置2个core, local[2]
      //接收socket的数据,需要一个对应的接收器,这个接收器就会使用cpu核
      //处理数据就需要一个执行器,这个执行器也会使用cpu核
      val conf = new SparkConf().setAppName("wordCount").setMaster("local[2]")
      //需要两个参数 第一个是 SparkConf  第二个是时间间隔,每个批次的数据的时间范围的长度
      val ssc = new StreamingContext(conf, Seconds(5)) //每5秒是一个时间批次
      ssc.checkpoint(".")
  
      //接收一个socket端口的数据
      //两个参数 第一参数:hostname或者是IP地址 第二个参数:端口号
      val lines = ssc.socketTextStream("Hadoop101", 9999)
      val words = lines.flatMap(_.split(" ")).map((_, 1))
      // 定义更新状态方法,参数values为当前批次单词频度,state为以往批次单词频度
      val updateFunc = (vales: Seq[Int], state: Option[Int]) => {
        val currentCount = vales.fold(0)(_ + _)
        val previousCount = state.getOrElse(0)
        Some(currentCount + previousCount)
      }
      //    val stateDstream = words.updateStateByKey[Int](updateFunc)
      //    stateDstream.print()
      //    val wordCount = words.reduceByKeyAndWindow(_+_,Seconds(15))
      //    wordCount.print()
      //    val wordCount1 = words.reduceByKeyAndWindow((a:Int,b:Int)=>a+b,Seconds(15),Seconds(10))
      //    wordCount1.print()
  
      //    val wordCount2 = words.countByWindow(Seconds(15),Seconds(5))
      //    wordCount2.print()
  
      //    val wordcount3 = words.countByValueAndWindow(Seconds(15),Seconds(5))
      //    wordcount3.print()
  
      val wordcount4 = words.window(Seconds(20))
      wordcount4.print()
      ssc.start()
      ssc.awaitTermination()
    }
  }

4.3count,countByValue

    object Demo3 {
      def main(args: Array[String]): Unit = {
        //注意运输模式为local的时候必须至少设置2个core, local[2]
        //接收socket的数据,需要一个对应的接收器,这个接收器就会使用cpu核
        //处理数据就需要一个执行器,这个执行器也会使用cpu核
        val conf = new SparkConf().setAppName("wordcount").setMaster("local[2]")
        //需要两个参数 第一个是 SparkConf  第二个是时间间隔,每个批次的数据的时间范围的长度
        val ssc = new StreamingContext(conf, Seconds(5))
        //每5秒是一个时间批次
        //接收一个socket端口的数据
        //两个参数 第一参数:hostname或者是IP地址 第二个参数:端口号
        val lines = ssc.socketTextStream("Hadoop101", 9999)
        //每个RDD所含元素的个数,统计所有value的共出现的个数
        val ds01 = lines.count()
        //统计每个RDD中,统计给每个value的出现的个数
        val ds02 = lines.countByValue()
        ds01.print()
        ds02.print()

        ssc.start()
        ssc.awaitTermination()
      }
    }

4.4reduceByKeyAndWindow

object Demo5 {
  def main(args: Array[String]): Unit = {
    //注意运输模式为local的时候必须至少设置2个core, local[2]
    //接收socket的数据,需要一个对应的接收器,这个接收器就会使用cpu核
    //处理数据就需要一个执行器,这个执行器也会使用cpu核
    val conf = new SparkConf().setAppName("wordcount").setMaster("local[2]")
    //需要两个参数 第一个是 SparkConf  第二个是时间间隔,每个批次的数据的时间范围的长度
    val ssc = new StreamingContext(conf, Seconds(5))
    ssc.checkpoint(".")

    //每5秒是一个时间批次
    //接收一个socket端口的数据
    //两个参数 第一参数:hostname或者是IP地址 第二个参数:端口号
    val lines = ssc.socketTextStream("Hadoop101", 9999)
    val words = lines.flatMap(_.split(" ")).map(x=>(x,1))

    //window(窗口时长) 窗口时长必须是批次的整数倍
//    val ds01 = lines.window(Seconds(10)) //返回 窗口时长 这个范围内的数据  窗口时长控制每次计算最近的多少个批次的数据
//    ds01.print()

    //window(窗口时长,滑动时长)
//    val ds02 = lines.window(Seconds(15),Seconds(10))
//    ds02.print()

    //reduceByKeyAndWindow(自定义函数,窗口时长,滑动时长)
//    val ds03 = words.reduceByKeyAndWindow((a:Int,b:Int)=>a+b,Seconds(15),Seconds(10))
//    ds03.print()

    val ds04 = words.countByWindow(Seconds(15),Seconds(10))
    val ds05 = words.countByValueAndWindow(Seconds(15),Seconds(10))
    ds04.print()
    ds05.print()
    ssc.start()
    ssc.awaitTermination()
  }
}

2021.11.26 10::42

posted @ 2021-11-26 10:46  哟喝  阅读(45)  评论(0)    收藏  举报