spark与kafka

本文深入探讨了Spark与Kafka的整合,包括Receiver模式和Direct模式的优缺点,以及如何解决数据丢失问题。同时,讨论了Spark的checkpoint原理机制,以及在数据处理中的并行度调整策略。此外,还介绍了Kafka的消费模型、数据分区、offset管理和数据一致性保障机制。
摘要由CSDN通过智能技术生成

spark

spark基础知识

  spark的任务提交流程

  shuffle过程分析

  rdd的特点与五大属性

spark整合kafka

1、SparkStreaming + Kafka ----Receiver

用的是Kafka高层次的消费者api,不能自己维护offset

object Sparkkafka08ReceiverDStream {
  def main(args: Array[String]): Unit = {
    val sparkContext = new SparkContext(new SparkConf()
      .setMaster("local[6]")
      .setAppName("08ReceiverDstream")
      .set("spark.streaming.receiver.writeAheadLog.enable","true"))  //开启WAL预写日志,保存数据源端安全性
    sparkContext.setLogLevel("WARN")
    val streamingContext = new StreamingContext(sparkContext,Seconds(5))

    //zk地址
    val zkQuorum = "node-1:2181,node-2:2181,node-3:2181"
    //消费组
    val groupId = "sparkkafka_group"
    //topic相关信息Map[String,Int],topic名称->分区数
    val topics = Map("kafkatopic"->3)

    //开启3个Receiver接收数据,每一个receiver都是一个独立的线程,去接收一个分区里面的数据
    val eachPatitionStream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      //使用receiverDstream的方式进行消费,消费模式使用的是kafka的HighLevelAPI,offset都保存在zk中
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(streamingContext, zkQuorum, groupId, topics) //只是相当于开启了一个receiver
      stream
    })
    //合并三个分区里面的数据
    //DStream里面保存的是一个元组,第一个数据是卡夫卡当中每条数据key,第二个数据是kafka当中每条数据value
    val result: DStream[(String, String)] = streamingContext.union(eachPatitionStream)
    result.print()
    streamingContext.start()
    streamingContext.awaitTermination()
  }
}
  • 流程分析
    当SparkStreaming application运行起来的时候,首先executor中会有一个receiver task接收kafka中推送过来的数据,将数据接收过来进行持久化,默认的持久化级别是:Memory_AND_DISK_SER_2,receiver task将数据进行储存和备份,这个过程中,会产生节点之间的数据传输,备份完成后去zookeeper中进行更新偏移量。然后向Driver节点上的receiver tracker汇报数据位置,最后Driver根据数据本地化将task发送到不同的executor上去执行。
  • 在执行的过程中,receiver task将数据的偏移量更新到zookeeper中,这时Driver节点宕机了,那么Driver下的Executor进程会被kill 掉,Executor可能有一部分数据未处理,就会存在找不到数据的问题,相当于数据丢失。
    问题解决:
    开启WAL(write ahead log)预写日志机制,当receiver task接收过来的数据接收到时,会首先备份到HDFS上一份(需要将接收过来的数据的持久化级别降低到MEMORY_ADN_DISK),并且向zookeeper更新信息,以及汇报位置等。这样解决了找不到的问题,但是由于向HDFS中写数据会影响job执行的效率。
  • 创建一个receiver接收器来对kafka进行定时拉取数据,这里产生的dstream中rdd分区和kafka的topic分区不是一个概念,故如果增加特定topic的分区数仅仅是增加一个receiver中消费topic的线程数,并没有增加spark的并行处理的数据量。
  • Receiver接收数据时,每隔spark.streaming.blockInterval=200ms,将接受类的数据封装到一个block,batchInterval内生成的这些block组成一个batch.
    假设batchInterval=5,默认一个batch25个block,这里的每一个block对应着RDD中的一个partition,并行度默认就是25个。
  • 如何提高并行度?
    在batchInterval一定的情况下,减少spark.streaming.blockInterval,可以增多对应RDD的分区数,并行度可以增加,一般blockInterval值不低于20ms.
  • receiver弊端
    1).采用了receiver接收器模式,将数据接收到executor中再进行处理,尤其是在任务有堆积情况下。内存占用很大。
    2).底层采用了高阶的API实现消费kafka,这种模式不关心offset,只要数据,针对需要维护offset的场景不适用。  
    3).存在丢失数据的问题,开启WAL可以避免,但是增加了数据处理的延迟。

2、SparkStreaming + Kafka ----Direct

Direct模式使用kafka的低阶API进行消费,这种API可以对offset进行操作,Receiver采用zookeeper管理offset,而Direct模式采用Spark自己管理,将offset存贮在topic中。

Direct模式相对于Receiver模式简化了并行度,Direct模式读取topic数据对应的并行度与topic的partition个数相同。也即是rdd的分区数?

kafka0.11之后彻底抛弃了reciver模式,可以手动提交offset,进行offset的管理维护,保证数据不会丢失,推荐使用。

/**
  * 使用lowLevelAPI进行消费,数据的offset都保存到topic里面去了
  * 这种整合方式不需要手动的开启多个receiver线程去消费数据
  */
object sparkkafka08DirectDStream {
  def main(args: Array[String]): Unit = {
    val sparkContext = new SparkContext(new SparkConf().setAppName("08DirectDStream").setMaster("local[6]"))
    sparkContext.setLogLevel("WARN")
    val streamingContext = new StreamingContext(sparkContext,Seconds(5))

    //配置kafka相关参数
    val kafkaParams = Map("metadata.broker.list"->"node-1:9092,node-2:9092,node-3:9092","group.id"->"Kafka_Direct")
    //要消费的topic名字
    val topics = Set("kafkatopic")

  val result: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](streamingContext,kafkaParams,topics)
    val topicDatas: DStream[String] = result.map(x=>x._2)
    //topicDatas.print()
    val wordNum: DStream[(String, Int)] = topicDatas.flatMap(x=>x.split(" ")).map((_,1)).reduceByKey(_+_)
    wordNum.print()

    streamingContext.start()
    streamingContext.awaitTermination()
  }
}



/**
  * 通过0.10版本用DirectDStream的方式实现数据的消费
  */
object kafka010DirectDStream {
  def main(args: Array[String]): Unit = {
    val sparkContext = new SparkContext(new SparkConf().setMaster("local[6]").setAppName("010DirectDStream"))
    sparkContext.setLogLevel("WARN")
    val streamingContext = new StreamingContext(sparkContext,Seconds(5))

    val locationStrategy: LocationStrategy = LocationStrategies.PreferConsistent

    val topics = Array("kafkatopic")

    val brokers= "node-1:9092,node-2:9092,node-3:9092"
    val sourcetopic="kafkatopic"
    //创建消费者组
    var group="sparkafkaGroup"
    //消费者配置
    val kafkaParam = Map(
      "bootstrap.servers" -> brokers,//用于初始化链接到集群的地址
      "key.deserializer" -> classOf[StringDeserializer],
      "value.deserializer" -> classOf[StringDeserializer],
      //用于标识这个消费者属于哪个消费团体
      "group.id" -> group,
      //如果没有初始化偏移量或者当前的偏移量不存在任何服务器上,可以使用这个配置属性
      //可以使用这个配置,latest自动重置偏移量为最新的偏移量
      "auto.offset.reset" -> "latest",
      //如果是true,则这个消费者的偏移量会在后台自动提交
      "enable.auto.commit" -> (false: java.lang.Boolean)
    );

    val consumerStrategy: ConsumerStrategy[String, String] = ConsumerStrategies.Subscribe(topics,kafkaParam)

    val result: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String,String](streamingContext,locationStrategy,consumerStrategy)

    //循环遍历每一个RDD当中的数据
    result.foreachRDD(iter => {
      if(iter.count() > 0){
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值