1.Reciver方式

image.png

基于Receiver方式,Spark内部使用 Kafka High Level API持续地从 Kafka 接收数据并存储在 Spark Executor的内存中,根据batch time触发job去消费接收到的数据,消费者不能自己去维护consumed offset,同时Kafka也不关心数据是否丢失。当向 Zookeeper 中更新完 offset 后,Driver 如果挂掉,那么 Driver 下的 Executors 就会被 kill 掉,会造成数据丢失。为了防止数据丢失,我们还需要在Spark Streaming中添加预写日志,这将同步地将所有接收到的Kafka数据保存到分布式文件系统(如HDFS)的预写日志中。

2.Direct方式

image.png

Spark 1.3中引入了这种新的无接收者的“Direct”方法,以确保更强的端到端保证。与使用接收者接收数据不同,该模式通过定期扫描所订阅的 Kafka 每个主题的每个分区的最新的 offset以确定当前批处理数据偏移范围。
相比 Receiver 方式,Direct 方式具有以下几个优势:
1.简单并行化:使用Direct, Spark stream将创建与Kafka分区一样多的RDD分区,这些分区将并行地从Kafka读取数据。
2.效率:在Receiver方法中,要实现零数据丢失,需要将数据存储在预写日志中。这实际上是低效的,数据会被有效地复制两次——一次由Kafka复制,第二次由Write Ahead日志复制。Direct方法消除了这个问题,因为没有接收器,因此不需要提前写日志。只要Kafka保留,消息可以从Kafka恢复。
3.Exactly-once语义:Receiver使用kafka high level Api在zookeeper中存储消耗的偏移量。Spark流可靠接收的数据和Zookeeper跟踪的偏移量之间可能产生不一致导致数据丢失。Direct不使用zookeeper,而是通过Spark Streaming的checkpoint进行追踪,不会有不一样的情况。

Scala代码:

import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
/**
 * @Author: ch
 * @Date: 11/07/2020 8:46 PM
 * @Version 1.0
 * @Describe:
 */
object SparkStreamingKMeansKafkaExample {

  def main(args: Array[String]) {
    val spark = SparkSession.builder()
      .appName("KafkaRecciver")
      .master("local[*]")
      .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
      .getOrCreate()
    val sc = spark.sparkContext
    val checkpointDir = "F:\\path"
    val ssc: StreamingContext = new StreamingContext(sc, Milliseconds(10))
    ssc.checkpoint("hdfs://host:9000/checkpoint")
    val kafkaParams: Map[String, Object] = Map[String, Object](
      "bootstrap.servers" -> "localhost:9092",
      "key.deserializer" -> classOf[StringDeserializer],
      "value.deserializer" -> classOf[StringDeserializer],
      "group.id" -> "use_a_separate_group_id_for_each_stream",
      "auto.offset.reset" -> "earliest",
      "enable.auto.commit" -> (false: java.lang.Boolean)
    )
    val topics = Array("occupancy")
    val stream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
      ssc,
      PreferConsistent,
      Subscribe[String, String](topics, kafkaParams)
    )
    val resultDStream = stream.map(x=>x.value())//从kafka读取出来的数据包含了很多信息,包括了信息时间戳等,所以要获取value值
    resultDStream.print()
    ssc.start()
    ssc.awaitTermination()
  }
}

kafkaParams可以看其官网的Streams API:http://kafka.apache.org/0100/...


ch123
60 声望7 粉丝

积土而为山,积水而为海。