Kafka为一个分布式的消息队列,spark流操作kafka有两种方式:

一种是利用接收器(receiver)和kafaka的高层API实现。

一种是不利用接收器,直接用kafka底层的API来实现(spark1.3以后引入)。

Receiver方式

基于Receiver方式实现会利用Kakfa的高层消费API,和所有的其他Receivers一样,接受到的数据会保存到excutors中,然后由spark Streaming 来启动Job进行处理这些数据。

在默认的配置下,这种方式在失败的情况下,会丢失数据,如果要保证零数据丢失,需要启用WAL(Write Ahead Logs)。它同步将接受到数据保存到分布式文件系统上比如HDFS。 所以数据在出错的情况下可以恢复出来。

使用两个步骤:

1、添加依赖:spark-streaming-kafka_2.10-1.3.0

2、编程:import org.apache.spark.streaming.kafka._

val kafkaStream = KafkaUtils.createStream(streamingContext,[ZK quorum], [consumer group id], [per-topic number of Kafka partitions to consume])

注意:

  • kafka的分区数和Spark的RDD的分区不是一个概念。所以在上述函数中增加特定主题的分区数,仅仅增加了一个receiver中消费topic的线程数,并不难增加spark并行处理数据的数量。

(那是不是多少个paratition最好对应多少个receiver的消费线程啊?)

  • 对于不同的group和topic,可以使用多个recivers创建多个DStreams来并行处理数据(如果是同一个topic如何保证数据不被重复消费?)
  • 如果启用了WAL,接收到的数据会被持久化一份到日志中,因此需要将storage_lever设置成StorgeLevel.MEMORY_AND_DISK_SER
    开启:

    val conf = new SparkConf()
    conf.set("spark.streaming.receiver.writeAheadLog.enable","true")
    val sc= new SparkContext(conf)
    val ssc = new StreamingContext(sc,Seconds(5))
    ssc.checkpoint("checkpoint")
    val lines = KafkaUtils.createStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicMap, StorageLevel.MEMORY_AND_DISK_SER).map(_._2)
     
    //开启在强行终止的情况下,数据仍然会丢失,解决办法:
    sys.addShutdownHook({
      ssc.stop(true,true)
    )})

3、运行

运行提交代码的时候,需要添加以下基本Jar包依赖:

 --jars lib/spark-streaming-kafka_2.10-1.3.0.jar,

    lib/spark-streaming_2.10-1.3.0.jar,

    lib/kafka_2.10-0.8.1.1.jar,lib/zkclient-0.3.jar,

4、例子

object KafkaWordCount {
  def main(args: Array[String]) {
    if (args.length < 4) {
      System.err.println("Usage: KafkaWordCount <zkQuorum> <group> <topics> <numThreads>")
      System.exit(1)
    }
  
    StreamingExamples.setStreamingLogLevels()
  
    val Array(zkQuorum, group, topics, numThreads) = args
    val sparkConf = new SparkConf().setAppName("KafkaWordCount")
    val ssc =  new StreamingContext(sparkConfSeconds(2))
    //保证元数据恢复,就是Driver端挂了之后数据仍然可以恢复
    ssc.checkpoint("checkpoint")
  
    val topicMap = topics.split(",").map((_,numThreads.toInt)).toMap
    val lines = KafkaUtils.createStream(ssc, zkQuorum, group, topicMap).map(_._2)
    val words = lines.flatMap(_.split(" "))
    val wordCounts = words.map(x => (x, 1L))
      .reduceByKeyAndWindow(_ + _, _ - _, Minutes(10), Seconds(2), 2)
    wordCounts.print()
  
    ssc.start()
    ssc.awaitTermination()
  }
}

5、图示:

<接收示意图>

<元数据恢复>

直接操作方式

不同于Receiver接收数据方式,这种方式定期从kafka的topic下对应的partition中查询最新偏移量,并在每个批次中根据相应的定义的偏移范围进行处理。Spark通过调用kafka简单的消费者API读取一定范围的数据。

相比基于Receiver方式有几个优点:

  • 简单的并发:

不需要创建多个kafka输入流,然后Union他们,而使用DirectStream,spark Streaming将会创建和kafka分区一样的RDD的分区数,而且会从kafka并行读取数据,Spark的分区数和Kafka的分区数是一一对应的关系。

  • 高效

第一种实现数据的零丢失是将数据预先保存在WAL中,会复制一遍数据,会导致数据被拷贝两次:一次是被Kafka复制;另一次是写入到WAL中,没有Receiver消除了这个问题。

  • 仅一次语义:

Receiver方式读取kafka,使用的是高层API将偏移量写入ZK中,虽然这种方法可以通过数据保存在WAL中保证数据的不对,但是可能会因为sparkStreaming和ZK中保存的偏移量不一致而导致数据被消费了多次,

第二种方式不采用ZK保存偏移量,消除了两者的不一致,保证每个记录只被Spark Streaming操作一次,即使是在处理失败的情况下。如果想更新ZK中的偏移量数据,需要自己写代码来实现。

1、引入依赖

同第一种方式。

2、编程

import org.apache.spark.streaming.kafka._
 
 
val directKafkaStream = KafkaUtils.createDirectStream[[key class], [value class], [key decoder class], [value decoder class] ](streamingContext, [map of Kafka parameters], [set of topics to consume])

如果想获得每个topic中每个分区的在spark streaming中的偏移量,可以通过以下代码:

directKafkaStream.foreachRDD { rdd =>
    val offsetRanges = rdd.asInstanceOf[HasOffsetRanges]
    // offsetRanges.length = # of Kafka partitions being consumed
    ...
}
//例子:
val ssc = new StreamingContext(sc, Seconds(2))
val kafkaParams = Map("zookeeper.connect" -> zkConnect,
      "group.id" -> kafkaGroupId,
      "metadata.broker.list" -> "10.15.42.23:8092,10.15.42.22:8092",
      "auto.offset.reset" -> "smallest"
    )
val topics = Set(topic)
 
val directKafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](
      ssc, kafkaParams, topics)
 
//KafkaCluster 需要从源码拷贝,此类是私有类。
directKafkaStream.foreachRDD(
 rdd => {
 val offsetLists = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
 val kc = new KafkaCluster(kafkaParams)
 for (offsets <- offsetLists) {
 val topicAndPartition = TopicAndPartition(offsets.topic, offsets.partition)
 val o = kc.setConsumerOffsets(kafkaGroupId, Map((topicAndPartition, offsets.untilOffset)))
 if (o.isLeft) {
 println(s"Error updating the offset to Kafka cluster: ${o.left.get}")
 }
 }
 }
)
 
 
 

3、部署:

同第一种方式。

4、图示:

 

SparkStreaming操作Kafka的更多相关文章

  1. spark-streaming集成Kafka处理实时数据

    在这篇文章里,我们模拟了一个场景,实时分析订单数据,统计实时收益. 场景模拟 我试图覆盖工程上最为常用的一个场景: 1)首先,向Kafka里实时的写入订单数据,JSON格式,包含订单ID-订单类型-订 ...

  2. SparkStreaming 整合kafka Demo

    这里使用的是低级API,因为高级API非常不好用,需要繁琐的配置,也不够自动化,却和低级API的效果一样,所以这里以低级API做演示 你得有zookeeper和kafka 我这里是3台节点主机 架构图 ...

  3. SparkStreaming获取kafka数据的两种方式:Receiver与Direct

    简介: Spark-Streaming获取kafka数据的两种方式-Receiver与Direct的方式,可以简单理解成: Receiver方式是通过zookeeper来连接kafka队列, Dire ...

  4. 【Spark】SparkStreaming和Kafka的整合

    文章目录 Streaming和Kafka整合 概述 使用0.8版本下Receiver DStream接收数据进行消费 步骤 一.启动Kafka集群 二.创建maven工程,导入jar包 三.创建一个k ...

  5. 图解SparkStreaming与Kafka的整合,这些细节大家要注意!

    前言 老刘是一名即将找工作的研二学生,写博客一方面是复习总结大数据开发的知识点,一方面是希望帮助更多自学的小伙伴.由于老刘是自学大数据开发,肯定会存在一些不足,还希望大家能够批评指正,让我们一起进步! ...

  6. SparkStreaming和Kafka基于Direct Approach如何管理offset实现exactly once

    在之前的文章<解析SparkStreaming和Kafka集成的两种方式>中已详细介绍SparkStreaming和Kafka集成主要有Receiver based Approach和Di ...

  7. Spark-读写HBase,SparkStreaming操作,Spark的HBase相关操作

    Spark-读写HBase,SparkStreaming操作,Spark的HBase相关操作 1.sparkstreaming实时写入Hbase(saveAsNewAPIHadoopDataset方法 ...

  8. SparkStreaming与Kafka,SparkStreaming接收Kafka数据的两种方式

    SparkStreaming接收Kafka数据的两种方式 SparkStreaming接收数据原理 一.SparkStreaming + Kafka Receiver模式 二.SparkStreami ...

  9. 大数据学习day32-----spark12-----1. sparkstreaming(1.1简介,1.2 sparkstreaming入门程序(统计单词个数,updateStageByKey的用法,1.3 SparkStreaming整合Kafka,1.4 SparkStreaming获取KafkaRDD的偏移量,并将偏移量写入kafka中)

    1. Spark Streaming 1.1 简介(来源:spark官网介绍) Spark Streaming是Spark Core API的扩展,其是支持可伸缩.高吞吐量.容错的实时数据流处理.Sp ...

随机推荐

  1. iOS 事件传递及响应过程

    iOS 事件传递及响应过程 -->>事件到来-->>事件分发 -->>事件响应 事件( Events) 官方文档( Events(iOS)) 是这样描写叙述的: U ...

  2. MVC3中给Html.TextAreaFor设置默认值(初始值)

    <div class="editor-field"> @Html.TextAreaFor(model => model.Comments) @Html.Valid ...

  3. exception http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application

      http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed w ...

  4. 【BIRT】在页面上展示xxxx年xx月xx日

    我们在做报表开发的时候经常会遇到一个问题,就是需要在报表上展示”xxxx年xx月xx日”这种日期,例如:需要在报表展示日期如下图: 我们现在数据库存储的日期是:20171231 那么我们如何转化为 这 ...

  5. try/except/else语句

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #Python学习手册 868 #try/except/else语句 #try语句分句形式 except: # ...

  6. 社区类 App 如何引导用户发帖和产生内容?

    作者:Pmer在路上链接:http://www.zhihu.com/question/25502904/answer/31342246来源:知乎著作权归作者所有,转载请联系作者获得授权. ugc的产出 ...

  7. HTML5 API—无刷新更新地址 history.pushState/replaceState方法(例子) (转)

    尽管是上面讲到的<JavaScript高级程序设计>(第二版)中提到,BOM中的location.path/query…… (window.location)在通过JavaScript更改 ...

  8. Android学习系列(2)--App自动更新之通知栏下载

    这篇文章是Android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用.1.设计思路,使用VersionCode定义为版本升级参数. android为我们定义版本提供了2个属性:& ...

  9. spring-integration-kafka

    1.pom.xml配置 <dependency> <groupId>org.springframework.integration</groupId> <ar ...

  10. Kafka日志清除策略

    一.更改日志输出级别 config/log4j.properties中日志的级别设置的是TRACE,在长时间运行过程中产生的日志大小吓人,所以如果没有特殊需求,强烈建议将其更改成INFO级别.具体修改 ...