这篇博客是基于Spark Streaming整合Kafka-0.8.2.1官方文档

  本文主要讲解了Spark Streaming如何从Kafka接收数据。Spark Streaming从Kafka接收数据主要有两种办法,一种是基于Kafka high-level API实现的基于Receivers的接收方式,另一种是从Spark 1.3版本之后新增的无Receivers的方式。这两种方式的代码编写,性能表现都不相同。本文后续部分对这两种方式逐一进行分析。

一、基于Receiver的模式

  这种模式主要会用到一个Receiver组件从Kafka接收数据,这个Receiver是基于Kafka的high-level消费者API实现的。Receivers从Kafka接收到的数据会保存在executors上,然后Spark Streaming启动Job来处理这些数据。

  然而,在默认配置情况下, 这种模式会有数据丢失的情况发生。为了实现零数据丢失,需要在Spark Streaming中启动Write Ahead Logs功能。WAL会同步的将所有从Kafka接收到的数据保存到一个分布式文件系统,比如HDFS上。用这种办法可以保证Spark Streaming从不可靠数据源获取数据失败时的恢复。

有关Write Ahead Logs的介绍,可以参考Streaming应用部署文档

  接下来讲解实际应用时如何实现这种模式。

1、依赖配置

  这种模式依赖的jar包相关信息如下

groupId = org.apache.spark
artifactId = spark-streaming-kafka-0-8_2.11
version = 2.0.1

2、程序编写

  需要导入KafkaUtils类来创建输入DStream。

import org.apache.spark.streaming.kafka._

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

  还可以指定输入数据key和value对于的解码类。可以参考KafkaUtilsAPI文档,或者Spark源码中提供的KafkaWordCount类。

需要注意的点是:

  1. Kafka中Topic的partitions和Spark Streaming中RDDs的partitions没有对应关系。所以增大KafkaUtils.createStream()方法中的特定主题的partitions数仅仅只会增加从单一Receiver接收并消费数据的线程数。并不会提供Spark并发处理数据的能力。
  2. 使用多个Receiver可以从Kafka的不同group和topic中读取数据生成多个输入DStream
  3. 如果启动了Write Ahead Logs功能,接收到的数据在处理之前已经做过备份。因此需要把输入数据流的存储级别调整为StorageLevel.MEMORY_AND_DISK_SER模式。即需要调用KafkaUtils.createStream方法时传入一个StorageLevel.MEMORY_AND_DISK_SER参数。

      Receivers和Write Ahead Logs功能的结合时,Spark Streaming应用使用Kafka高阶API将消费offsets保存在Zookeeper中。虽然这种使用方式可以保证避免数据丢失,但是不能保证在某些失败情况下数据被多次处理,即这种情况下实现的是At Least Once。因为Spark Streaming读取数据的Offset都是由Zookeeper来维护的。这样在Spak Streaming和Zookeeper维护offsets的过程中无法保证其同步。

3、应用运行

  和其他Spark应用程序一样,Spark Streaming应用也可以用spark-submit来启动。

  需要将依赖的spark-streaming-kafka-0.8_2.11以及该JAR包的依赖包都需要打入应用所在的JAR包中。并且要保证运行环境中提供了spark-core_2.11以及spark-streaming_2.11

  也可以使用spark-submit--jars参数引入依赖的spark-streaming-kafka-0-8_2.11引入。

二、直接模式(无Receiver)模式

  这种模式下不需要使用Receivers从Kafka接收数据,这种模式下Streaming应用会定期的查询每一个Kafka Topic的Partitions最新的消费Offsets,基于这些Offsets数据来定义每一个batch需要处理的数据范围。有了这些Offset范围后,Streaming应用汇使用Kafka的Simple Consumer API从Kafka读取数据。

  这种模式相比于基于Receivers的模式有以下优点: 

  1. 并发更加简单:不再需要定义多个Kafka输入DStream然后将多个输入合并。通过使用directStream,Spark Streaming会创建与Kafka partitions个数相同的RDD partitions来接收数据,这些partitions会并发的从Kafka读取数据。所以在这种模式下,Kafka Partition和RDD Partitions有一一对应关系。这种对应更好理解与调试。
  2. 高效:由于没有Receiver,所以也不需要启用Write Ahead Logs功能。失败重试时可以直接从Kafka中读取数据。
  3. 保证了Exactly-Once:这种模式下,读取数据不通过Zookeeper。Offsets由Spark Streaming应用程序维护并可以记录在检查点中。所以这保证了Spark Streaming数据读取的exactly once。如果想要实现计算结果输出的exactly once,应用程序中保存计算数据和offsets到外部数据系统的操作必须具有幂等性 (idempotent)或原子事物性(atomic transaction)。可以参考Spark Streaming输出操作语义

      这种模式的一个缺点是它不会更新Zookeeper中的offsets状态,所以那些基于Zookeeper的Kafka监控工具在这种情况下会失效,比如KafkaOffsetsMonitor等。然而如果在应用程序中可以手动的获取每一batch的offset,并手动更新到Zookeeper中去。

      接下来讲解实际应用时如何实现这种模式。

1、依赖配置

  这种模式依赖的jar包相关信息如下

groupId = org.apache.spark
artifactId = spark-streaming-kafka-0-8_2.11
version = 2.0.1

2、程序编写

  需要导入KafkaUtils类来创建输入DStream。

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])

  可以为createDirectStream方法传入一个messageHandler对象来访问MessageAndMetadata,这个MessageAndMetadata对当前message的metadata进行结构化。有关该方法的使用,可以仔细阅读API文档或者Spark源码中提供的DirectKafkaWordCount示例程序。

  在Kafka参数[map of Kafka parameters]中,必须指定的是metadata.broker或者bootstrap.servers。默认情况下,会从每一个Kafka partition的最新offset开始消费。如果在这里将auto.offset.reset设置成smallest的话,Spark Streaming将从最小offset开始消费。

  也可以往KafkaUtils.createDirectStream方法中传入offset参数从任意offset处开始消费。按照下面代码中的方式,可以获取每一个batch对应的offset状况。

// Hold a reference to the current offset ranges, so it can be used downstream
var offsetRanges = Array[OffsetRange]() directKafkaStream.transform { rdd =>
offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
rdd
}.map {
...
}.foreachRDD { rdd =>
for (o <- offsetRanges) {
println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
}
...
}

  获取到Offset信息后,如果有需要,可以手动将这些数据写入Zookeeper中。

需要注意的是:

  1. 上面代码中,HasOffsetRanges的类型转换只有当directKafkaStream上的第一个方法执行成功后才能成功。所以,如果想要获取offsets,可以在输入DStream的第一个方法调用处使用transform,获取到offset后,再调用其他方法处理该DStream,正如上面代码所示。
  2. 前面提到在这种模式下Kafka和RDD的Partitions一一对应,但是如果在Spark Streaming应用程序中执行了shuffle或者repartition操作,比如reduceByKey或者window操作后,这种对应关系就不存在了。
  3. 由于这种模式没有Receivers,所以Spark配置参数中那些receiver相关的参数在这种模式下不会起作用,比如spark.streaming.recerver.*参数。此时应该配置的参数是spark.streaming.kafka.*,在这些参数里面很重要的一个是spark.streaming.kafka.maxRatePerPartition,这个参数的作用是控制Streaming程序通过Kafka direct API每个partition每秒读入的消息最大数。这个参数在程序初次运行时特别重要。如果不设置这个参数,在Streaming启动时如果将offsets设置为smallest,第一个batch将会读入所有数据,导致后续batch长时间卡住。

3、应用运行

  和第一种模式相同。

Spark Streaming + Kafka整合(Kafka broker版本0.8.2.1+)的更多相关文章

  1. spark streaming中维护kafka偏移量到外部介质

    spark streaming中维护kafka偏移量到外部介质 以kafka偏移量维护到redis为例. redis存储格式 使用的数据结构为string,其中key为topic:partition, ...

  2. Zookeeper+Kafka+Spark streaming单机整合开发

    环境准备: ubuntu 开发环境: jdk 1.8 scala:2.11.0 spark 2.0 zookeeper 3.4.6 kafka  2.12-0.10.2.0 开始整合: 1 zooke ...

  3. Spark Streaming 实现读取Kafka 生产数据

    在kafka 目录下执行生产消息命令: ./kafka-console-producer  --broker-list nodexx:9092 --topic  201609 在spark bin 目 ...

  4. Spark Streaming的接收KAFKA的数据

    https://github.com/lw-lin/CoolplaySpark/blob/master/Spark%20Streaming%20%E6%BA%90%E7%A0%81%E8%A7%A3% ...

  5. Flink与Spark Streaming在与kafka结合的区别!

    本文主要是想聊聊flink与kafka结合.当然,单纯的介绍flink与kafka的结合呢,比较单调,也没有可对比性,所以的准备顺便帮大家简单回顾一下Spark Streaming与kafka的结合. ...

  6. Spark Streaming + Flume整合官网文档阅读及运行示例

    1,基于Flume的Push模式(Flume-style Push-based Approach)      Flume被用于在Flume agents之间推送数据.在这种方式下,Spark Stre ...

  7. Kafka学习之broker配置(0.8.1版)(转)

    broker.id  默认值:无 每一个broker都有一个唯一的id,这是一个非负整数,这个id就是broker的"名字",这样就允许broker迁移到别的机器而不会影响消费者. ...

  8. Exactly-once Spark Streaming from Apache Kafka

    这篇文章我已经看过两遍了.收获颇多,抽个时间翻译下,先贴个原文链接吧.也给自己留个任务 http://blog.cloudera.com/blog/2015/03/exactly-once-spark ...

  9. Spark官方3 ---------Spark Streaming编程指南(1.5.0)

    Design Patterns for using foreachRDD dstream.foreachRDD是一个强大的原语,允许将数据发送到外部系统.然而,了解如何正确有效地使用该原语很重要.避免 ...

随机推荐

  1. 面向面试题和实际使用谈promise

    "金三银四,金九银十",都是要收获的季节.面对各种面试题,各种概念.原理都要去记,挺枯燥的.本文是面向面试题和实际使用谈一下Promise. Promise是什么? Promise ...

  2. OS模块文件操作一

    1          文件操作 1.1               OS模块 l  import os  #引入os模块 l  import os.path  #引入os下的path子模块 l  os ...

  3. 如何用.reg文件操作注册表

    Windows Registry Editor Version 5.00 ;删除值 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpi ...

  4. PHP性能优化利器:生成器 yield理解

    如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生.但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明显.但是,生成 ...

  5. java面试2(java技术栈和Hollis面试内容分享)

    1.什么是java虚拟机? java虚拟机(JVM)是一个可执行java字节码的虚拟机进程,java源文件被编译成能被java虚拟机可执行的字节码文件. 2.什么是平台无关性,java是如何做到平台无 ...

  6. JS 变量类型互相转换

    转载自:http://zhaizhiyuan.blog.163.com/blog/static/1897672632009093147268/ Java中几种常用的数据类型之间转换方法:  1. sh ...

  7. [ZJOI 2007]Hide 捉迷藏

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双 ...

  8. [NOIp 2016]换教室

    Description 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 $2n$ 节课程安排在 $n$ 个时间段上.在第 $i$($1 \leq ...

  9. hdu 5451(矩阵 +Fibonacci )

    题意:求 [(5 + 2*sqrt(6))^(1 + 2^x)]  % M 基于hdu2256可以求(5 + 2*sqrt(6))^ n 但是n特别大,我们可以找矩阵的循环节 两种可能 1.mod-1 ...

  10. [bzoj4815]: [Cqoi2017]小Q的表格

    来自FallDream的博客,未经允许,请勿转载,谢谢. 小Q是个程序员. 作为一个年轻的程序员,小Q总是被老C欺负,老C经常把一些麻烦的任务交给小Q来处理.每当小Q不知道如何解决时,就只好向你求助. ...