[大数据]Spark(4)SparkStreaming

1.Spark Streaming

1.1 离线和实时概念

数据处理的延迟
离线计算
就是在计算开始前已知所有输入数据,输入数据不会产生变化,一般计算量级较大,计算时间也较长。例如今天早上一点,把昨天累积的日志,计算出所需结果。最经典的就是Hadoop的MapReduce方式;
实时计算
输入数据是可以以序列化的方式一个个输入并进行处理的,也就是说在开始的时候并不需要知道所有的输入数据。与离线计算相比,运行时间短,计算量级相对较小。强调计算过程的时间要短,即所查当下给出结果。

1.2 批量和流式概念

数据处理的方式
批:处理离线数据,冷数据。单个处理数据量大,处理速度比流慢。
流:在线,实时产生的数据。单次处理的数据量小,但处理速度更快。
近年来,在Web应用、网络监控、传感监测等领域,兴起了一种新的数据密集型应用——流数据,即数据以大量、快速、时变的流形式持续到达。实例:PM2.5检测、电子商务网站用户点击流。
流数据具有如下特征:
数据快速持续到达,潜在大小也许是无穷无尽的
数据来源众多,格式复杂
数据量大,但是不十分关注存储,一旦经过处理,要么被丢弃,要么被归档存储
注重数据的整体价值,不过分关注个别数据

1.3 Spark Streaming

Spark Streaming用于流式数据的处理。Spark Streaming支持的数据输入源很多,例如:Kafka、Flume、Twitter、ZeroMQ和简单的TCP套接字等等。数据输入后可以用Spark的高度抽象算子如:map、reduce、join、window等进行运算。而结果也能保存在很多地方,如HDFS,数据库等。
在这里插入图片描述
在 Spark Streaming 中,处理数据的单位是一批而不是单条,而数据采集却是逐条进行的,因此 Spark Streaming 系统需要设置间隔使得数据汇总到一定的量后再一并操作,这个间隔就是批处理间隔。批处理间隔是Spark Streaming的核心概念和关键参数,它决定了Spark Streaming提交作业的频率和数据处理的延迟,同时也影响着数据处理的吞吐量和性能。
在这里插入图片描述
和Spark基于RDD的概念很相似,Spark Streaming使用了一个高级抽象离散化流(discretized stream),叫作DStreams。DStreams是随时间推移而收到的数据的序列。在内部,每个时间区间收到的数据都作为RDD存在,而DStreams是由这些RDD所组成的序列(因此得名“离散化”)。DStreams可以由来自数据源的输入数据流来创建, 也可以通过在其他的 DStreams上应用一些高阶操作来得到。

SparkStreaming原理
在这里插入图片描述

1.4 Spark Streaming特点

1.4.1 易用

在这里插入图片描述

1.4.2 容错

在这里插入图片描述

1.4.3 易整合到Spark体系

在这里插入图片描述

1.4.4 缺点

Spark Streaming是一种“微量批处理”架构, 和其他基于“一次处理一条记录”架构的系统相比, 它的延迟会相对高一些。

1.5 Spark Streaming架构‘

1.5.1 架构图

SparkStreaming架构图:
在这里插入图片描述
整体架构图:
在这里插入图片描述

1.5.2 背压机制

Spark 1.5以前版本,用户如果要限制Receiver的数据接收速率,可以通过设置静态配制参数“spark.streaming.receiver.maxRate”的值来实现,此举虽然可以通过限制接收速率,来适配当前的处理能力,防止内存溢出,但也会引入其它问题。比如:producer数据生产高于maxRate,当前集群处理能力也高于maxRate,这就会造成资源利用率下降等问题。
为了更好的协调数据接收速率与资源处理能力,1.5版本开始Spark Streaming可以动态控制数据接收速率来适配集群数据处理能力。背压机制(即Spark Streaming Backpressure): 根据JobScheduler反馈作业的执行信息来动态调整Receiver数据接收率。
通过属性“spark.streaming.backpressure.enabled”来控制是否启用backpressure机制,默认值false,即不启用。

2.DStream入门

2.1 WordCount案例实操

  1. 需求:使用netcat工具向9999端口不断的发送数据,通过SparkStreaming读取端口数据并统计不同单词出现的次数
    2)添加依赖:
<dependency>
	<groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming_2.11</artifactId>
    <version>2.1.1</version>
</dependency>

3)编写代码

object SparkStreaming01_WordCount {
  def main(args: Array[String]): Unit = {
    //创建配置文件对象, Streaming程序执行至少需要两个线程,不能设置为local
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming01_WordCount").setMaster("local[*]")
    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期

    //从指定端口获取数据
    val socketDS: ReceiverInputDStream[String] = ssc.socketTextStream("hadoop102", 9999)

    //扁平化
    val flatMapDS: DStream[String] = socketDS.flatMap(_.split(" "))

    //结构转换,进行计数
    val mapDS: DStream[(String, Int)] = flatMapDS.map((_, 1))

    //聚合
    val reduceDS: DStream[(String, Int)] = mapDS.reduceByKey(_ + _)

    //打印输出
    reduceDS.print()

    //启动采集器
    ssc.start()

    //默认情况下,采集器不能关闭
    //ssc.stop()

    //等待采集结束之后,终止程序
    ssc.awaitTermination()
  }

}

执行结果:

在这里插入图片描述
输出:

-------------------------------------------
Time: 1628068242000 ms
-------------------------------------------
(aaa,1)

-------------------------------------------
Time: 1628068245000 ms
-------------------------------------------

-------------------------------------------
Time: 1628068248000 ms
-------------------------------------------
(bbb,1)

-------------------------------------------
Time: 1628068251000 ms
-------------------------------------------
(ddd,1)
(ccc,1)

-------------------------------------------
Time: 1628068254000 ms
-------------------------------------------

-------------------------------------------
Time: 1628068257000 ms
-------------------------------------------

-------------------------------------------
Time: 1628068260000 ms
-------------------------------------------
(fff,1)

2.2 WordCount 解析

Discretized Stream是Spark Streaming的基础抽象,代表持续性的数据流和经过各种Spark算子操作后的结果数据流。在内部实现上,DStream是一系列连续的RDD来表示,每个RDD含有一段时间间隔内的数据,对这些 RDD的转换是由Spark引擎来计算的, DStream的操作隐藏的大多数的细节, 然后给开发者提供了方便使用的高级 API如下图:

WordCount数据流解析:

在这里插入图片描述

2.3 几点注意

一旦StreamingContext已经启动, 则不能再添加新的 streaming computations
一旦一个StreamingContext已经停止(StreamingContext.stop()), 他也不能再重启
在一个 JVM 内, 同一时间只能启动一个StreamingContext
stop() 的方式停止StreamingContext, 也会把SparkContext停掉. 如果仅仅想停止StreamingContext, 则应该这样: stop(false)
一个SparkContext可以重用去创建多个StreamingContext, 前提是以前的StreamingContext已经停掉,并且SparkContext没有被停掉

3.DStream创建

3.1 RDD队列

3.1.1 用法及说明

测试过程中,可以通过使用ssc.queueStream(queueOfRDDs)来创建DStream,每一个推送到这个队列中的RDD,都会作为一个DStream处理。

3.1.2 案例实操

1)需求:循环创建几个RDD,将RDD放入队列。通过SparkStream创建Dstream,计算WordCount
2)编写代码:

/**
 * 通过RDD队列方式创建DStream
 * 需求:循环创建几个RDD,将RDD放入队列。通过SparkStream创建DStream,计算WordCount
 */
object SparkStreaming02_RDDQueue {
  def main(args: Array[String]): Unit = {
    //创建配置文件对象, Streaming程序执行至少需要两个线程,不能设置为local
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming02_RDDQueue").setMaster("local[*]")

    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期

    //创建RDD队列
    val rddQueue: mutable.Queue[RDD[Int]] = new mutable.Queue[RDD[Int]]()

    //从队列中采集数据,获取DS
    val queueDS: InputDStream[Int] = ssc.queueStream(rddQueue)

    //处理采集到的数据
    val resDS: DStream[(Int, Int)] = queueDS.map((_, 1)).reduceByKey(_ + _)

    //打印结果
    resDS.print()

    //启动采集器
    ssc.start()

    //循环创建RDD,并将创建的RDD放到队列中去
    for (i <- 1 to 3) {
      rddQueue.enqueue(ssc.sparkContext.makeRDD(6 to 10))
      Thread.sleep(2000)
    }

    ssc.awaitTermination()
  }

}

输出:每隔三秒采集到一次,只能采集到前三次

-------------------------------------------
Time: 1628071287000 ms
-------------------------------------------
(6,1)
(7,1)
(8,1)
(9,1)
(10,1)

-------------------------------------------
Time: 1628071290000 ms
-------------------------------------------
(6,1)
(7,1)
(8,1)
(9,1)
(10,1)

-------------------------------------------
Time: 1628071293000 ms
-------------------------------------------
(6,1)
(7,1)
(8,1)
(9,1)
(10,1)

3.2 自定义数据源

3.2.1 用法及说明

需要继承Receiver,并实现onStart、onStop方法来自定义数据源采集。

3.2.2 案例实操

/**
 * 通过自定义数据源方式创建DStream
 * 模拟从指定的网络端口获取数据
 */

object SparkStreaming03_CustomerReceiver {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming03_CustomerReceiver").setMaster("local[*]")

    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期

    //通过自定义数据源创建DStream
    val myDS: ReceiverInputDStream[String] = ssc.receiverStream(new MyReceiver("hadoop102", 9999))

    //扁平化
    val flatMapDs: DStream[String] = myDS.flatMap(_.split(" "))

    //结构转换,进行计数
    val mapDS: DStream[(String, Int)] = flatMapDs.map((_, 1))

    //聚合
    val reduceDS: DStream[(String, Int)] = mapDS.reduceByKey(_ + _)

    //打印输出
    reduceDS.print()

    ssc.start()
    ssc.awaitTermination()

  }
}

//Receiver[T] 泛型表示读到的数据类型
class MyReceiver(host: String, port: Int) extends Receiver[String](StorageLevel.MEMORY_ONLY) {

  private var socket: Socket = _

  override def onStart(): Unit = {
    new Thread("Socket Receiver") {
      setDaemon(true)

      override def run() {
        receive()
      }
    }.start()
  }

  //读数据并将数据发送给Spark
  def receive(): Unit = {
    try {
      //创建连接
      socket = new Socket(host, port)
      
      //根据对象获取输入流
      val reader: BufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8))
      
      //定义一个变量用于接收到读取的一行数据
      var input: String = null
      while ((input = reader.readLine()) != null) {
        store(input) //采集到的数据传给store
      }

    } catch {
      case e: ConnectException =>
        restart(s"Error connecting to $host:$port", e)
        return
    } finally {}
    onStop()
  }

  override def onStop(): Unit = {
    if (socket != null) {
      socket.close()
      socket = null
    }
  }
}

向hadoop102:9999发送数据
在这里插入图片描述

输出:

-------------------------------------------
Time: 1628075637000 ms
-------------------------------------------
(ddd,1)
(ccc,1)
(bbb,2)
(aaa,2)

3.3 Kafka数据源

3.3.1 版本选型

ReceiverAPI:需要一个专门的Executor去接收数据,然后发送给其他的Executor做计算。存在的问题,接收数据的Executor和计算的Executor速度会有所不同,特别在接收数据的Executor速度大于计算的Executor速度,会导致计算数据的节点内存溢出。

DirectAPI:是由计算的Executor来主动消费Kafka的数据,速度由自身控制。
请添加图片描述

3.3.2 Kafka 0-8 Receive模式

1)需求:通过SparkStreaming从Kafka读取数据,并将读取过来的数据做简单计算,最终打印到控制台。
2)导入依赖:

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
    <version>2.1.1</version>
</dependency>

3)编写代码

/**
 * 通过ReceiverAPI连接kafka数据源,获取数据
 */
object SparkStreaming04_ReceiverAPI {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming04_ReceiverAPI").setMaster("local[*]")

    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期

    //连接kafka创建DStream
    val kafkaDstream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(
      ssc,
      "hadoop102:2181,hadoop103:2181,hadoop104:2181",
      "bigdata",
      Map("bigdata01" -> 2)
    )
    //获取kafka中的消息,只需要v的部分
    val lineDS: DStream[String] = kafkaDstream.map(_._2)

    //扁平化
    val flatMapDS: DStream[String] = lineDS.flatMap(_.split(" "))

    //结构转换,进行计数
    val mapDS: DStream[(String, Int)] = flatMapDS.map((_, 1))

    //聚合
    val reduceDS: DStream[(String, Int)] = mapDS.reduceByKey(_ + _)

    reduceDS.print()

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

4)向kafka生产者发送数据

#创建topic
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh --create --bootstrap-server hadoop102:9092 --topic bigdata01 --partitions 2 --replication-factor 2
#查看topic列表
[atguigu@hadoop102 kafka]$ bin/kafka-topics.sh --list --bootstrap-server hadoop102:9092
#向生产者发送数据
[atguigu@hadoop102 kafka]$ bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic bigdata01
>hello world
>aaa aaa

程序接收输出:

-------------------------------------------
Time: 1628081490000 ms
-------------------------------------------
(world,1)
(hello,1)

-------------------------------------------
Time: 1628081499000 ms
-------------------------------------------
(aaa,2)

0-8Receive模式,offset维护在zk中,程序停止后,继续生产数据,再次启动程序,仍然可以继续消费。可通过get /consumers/bigdata/offsets/主题名/分区号 查看

停止程序后继续发送数据

>a a a a a 
>b b b
>c c
>

重启程序还是可以继续消费

-------------------------------------------
Time: 1628081874000 ms
-------------------------------------------
(a,5)
(b,3)
(c,2)

3.3.3 Kafka 0-8 Direct模式

1)需求:通过SparkStreaming从Kafka读取数据,并将读取过来的数据做简单计算,最终打印到控制台。
2)导入依赖:

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
    <version>2.1.1</version>
</dependency>

3)代码编写

(自动维护offset1)
offset维护在checkpoint中,但是获取StreamingContext的方式需要改变,目前这种方式会丢失消息。

/**
 * 通过DirectAPI连接Kafka数据源,获取数据
 * 自定义维护偏移量,维护在checkpoint中
 * 目前这个版本只是指定检查点,只会将offset放到检查点中,但是并没有从检查点中取,数据还是会丢失
 */
object SparkStreaming05_DirectAPI_Auto01 {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming05_DirectAPI_Auto01").setMaster("local[*]")

    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期

    //设置检查点目录
    ssc.checkpoint("E:\\spark-0701\\cp")

    //准备Kafka参数
    val kafkaParams: Map[String, String] = Map[String, String](
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "hadoop102:9092,hadoop103:9092,hadoop104:9092",
      ConsumerConfig.GROUP_ID_CONFIG -> "bigdata"
    )
    val kafkaDstream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](
      ssc,
      kafkaParams,
      Set("bigdata01")
    )

    //获取kafka中的消息,只需要v的部分
    val lineDS: DStream[String] = kafkaDstream.map(_._2)

    //扁平化
    val flatMapDS: DStream[String] = lineDS.flatMap(_.split(" "))

    //结构转换,进行计数
    val mapDS: DStream[(String, Int)] = flatMapDS.map((_, 1))

    //聚合
    val reduceDS: DStream[(String, Int)] = mapDS.reduceByKey(_ + _)

    reduceDS.print()

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

(自动维护offset2)
offset维护在checkpoint中,获取StreamingContext为getActiveOrCreate
这种方式缺点:
checkpoint小文件过多
checkpoint记录最后一次时间戳,再次启动的时候会把间隔时间的周期再执行一次

/**
 * 通过DirectAPI连接Kafka数据源,获取数据
 * 自定义维护偏移量,维护在checkpoint中
 * 修改StreamingContext对象的获取方式
 */
object SparkStreaming05_DirectAPI_Auto02 {
  def main(args: Array[String]): Unit = {
    //创建Streaming上下文环境 如果存在检查点,从检查点中取,如果没有,则自己创建
    val ssc: StreamingContext = StreamingContext.getActiveOrCreate("E:\\spark-0701\\cp", () => getStreamingContext)

    ssc.start()
    ssc.awaitTermination()
  }

  def getStreamingContext(): StreamingContext = {
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming05_DirectAPI_Auto02").setMaster("local[*]")

    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期

    //设置检查点目录
    ssc.checkpoint("E:\\spark-0701\\cp")

    //准备Kafka参数
    val kafkaParams: Map[String, String] = Map[String, String](
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "hadoop102:9092,hadoop103:9092,hadoop104:9092",
      ConsumerConfig.GROUP_ID_CONFIG -> "bigdata"
    )
    val kafkaDstream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](
      ssc,
      kafkaParams,
      Set("bigdata01")
    )

    //获取kafka中的消息,只需要v的部分
    val lineDS: DStream[String] = kafkaDstream.map(_._2)

    //扁平化
    val flatMapDS: DStream[String] = lineDS.flatMap(_.split(" "))

    //结构转换,进行计数
    val mapDS: DStream[(String, Int)] = flatMapDS.map((_, 1))

    //聚合
    val reduceDS: DStream[(String, Int)] = mapDS.reduceByKey(_ + _)

    //打印输出
    reduceDS.print()

    ssc
  }
}

发送消息:

>a a a a a 
#停止程序
>b b b
>c c

输出

-------------------------------------------
Time: 1628084280000 ms
-------------------------------------------
(a,5)

#重启之后
-------------------------------------------
Time: 1628084283000 ms
-------------------------------------------
(b,3)
(c,2)

(手动维护offset)

/**
 * 通过DirectAPI连接Kafka数据源,获取数据
 * 手动维护offset
 */
object SparkStreaming07_DirectAPI_Hander {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("SparkStreaming07_DirectAPI_Hander").setMaster("local[*]")

    //创建SparkStreaming程序执行入口对象(上下文环境对象)
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3)) //配置文件,执行周期


    //准备Kafka参数
    val kafkaParams: Map[String, String] = Map[String, String](
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "hadoop102:9092,hadoop103:9092,hadoop104:9092",
      ConsumerConfig.GROUP_ID_CONFIG -> "bigdata"
    )

    //获取上一次消费的位置(偏移量)
    //实际项目中,为了保证数据精准一致性,我们对数据进行消费处理之后,将偏移量保存在有事务的存储中,如MySQL

    val fromOffsets: Map[TopicAndPartition, Long] = Map[TopicAndPartition, Long](
      TopicAndPartition("bigdata01", 0) -> 13L,
      TopicAndPartition("bigdata01", 1) -> 10L
    )

    //使用DirectAPI手动维护offset的方式消费数据
    val kafkaDstream: InputDStream[String] = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder, String](
      ssc,
      kafkaParams,
      fromOffsets,
      (m: MessageAndMetadata[String, String]) => m.message())

    //消费完毕之后,对偏移量offset进行更新

    var offsetRanges = Array.empty[OffsetRange]
    kafkaDstream.transform {
      rdd =>
        offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges //拿到对应数据的偏移量
        rdd
    }.foreachRDD {
      rdd => {
        for (elem <- offsetRanges) {
          println(s"${elem.topic} ${elem.partition} ${elem.fromOffset} ${elem.untilOffset}")
        }
      }
    }

    //开启任务
    ssc.start()
    ssc.awaitTermination()
  }
}

3.3.4 Kafka 0-10 Direct模式

1)需求:通过SparkStreaming从Kafka读取数据,并将读取过来的数据做简单计算,最终打印到控制台。
2)导入依赖:新建一个module

<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-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.1.1</version>
</dependency>

3)编写代码

import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.{SPARK_BRANCH, SparkConf}
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object SparkStream01_DirectAPI010 {
  def main(args: Array[String]): Unit = {
    //创建配置文件对象
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkStream01_DirectAPI010")

    //创建StreamingContext对象
    val ssc = new StreamingContext(conf, Seconds(3))

    //定义kafka相关的连接参数
    val kafkaParams: Map[String, Object] = Map[String, Object](
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "hadoop102:9092,hadoop103:9092,hadoop104:9092",
      ConsumerConfig.GROUP_ID_CONFIG -> "bigdata",
      ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer",
      ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer]
    )
    //通过读取kafka数据,创建DStream
    val kafkaDStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
      ssc,
      //位置策略,计算指定的Excutor
      LocationStrategies.PreferConsistent, //给yarn上所有节点分配
      //消费策略
      ConsumerStrategies.Subscribe[String, String](Set("bigdata01"), kafkaParams)
    )

    kafkaDStream.map(_.value()).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).print()

    //启动采集任务
    ssc.start()
    ssc.awaitTermination()
  }
}

发送消息

>aaa

输出:

-------------------------------------------
Time: 1628251077000 ms
-------------------------------------------
(aaa,1)

重启程序,也可以继续消费,偏移量存放在 kafka _consumer_offsets主题中

3.3.5 消费Kafka数据模式总结

0-8 ReceiverAPI:

  1. 专门的Executor读取数据,速度不统一
  2. 跨机器传输数据,WAL
  3. Executor读取数据通过多个线程的方式,想要增加并行度,则需要多个流union
  4. offset存储在Zookeeper中
    0-8 DirectAPI:
  5. Executor读取数据并计算
  6. 增加Executor个数来增加消费的并行度
  7. offset存储
    3.1) CheckPoint(getActiveOrCreate方式创建StreamingContext)
    3.2) 手动维护(有事务的存储系统)
    3.3) 获取offset必须在第一个调用的算子中:offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
    0-10 DirectAPI:
  8. Executor读取数据并计算
  9. 增加Executor个数来增加消费的并行度
  10. offset存储
    i. a.__consumer_offsets系统主题中
    ii. b.手动维护(有事务的存储系统)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值