Spark Streaming + Kafka整合(Kafka broker版本0.8.2.1+)
这篇博客是基于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类。
需要注意的点是:
- Kafka中Topic的partitions和Spark Streaming中RDDs的partitions没有对应关系。所以增大
KafkaUtils.createStream()方法中的特定主题的partitions数仅仅只会增加从单一Receiver接收并消费数据的线程数。并不会提供Spark并发处理数据的能力。 - 使用多个Receiver可以从Kafka的不同group和topic中读取数据生成多个输入DStream
如果启动了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的模式有以下优点:
- 并发更加简单:不再需要定义多个Kafka输入DStream然后将多个输入合并。通过使用
directStream,Spark Streaming会创建与Kafka partitions个数相同的RDD partitions来接收数据,这些partitions会并发的从Kafka读取数据。所以在这种模式下,Kafka Partition和RDD Partitions有一一对应关系。这种对应更好理解与调试。 - 高效:由于没有Receiver,所以也不需要启用Write Ahead Logs功能。失败重试时可以直接从Kafka中读取数据。
保证了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中。
需要注意的是:
- 上面代码中,
HasOffsetRanges的类型转换只有当directKafkaStream上的第一个方法执行成功后才能成功。所以,如果想要获取offsets,可以在输入DStream的第一个方法调用处使用transform,获取到offset后,再调用其他方法处理该DStream,正如上面代码所示。 - 前面提到在这种模式下Kafka和RDD的Partitions一一对应,但是如果在Spark Streaming应用程序中执行了
shuffle或者repartition操作,比如reduceByKey或者window操作后,这种对应关系就不存在了。 - 由于这种模式没有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+)的更多相关文章
- spark streaming中维护kafka偏移量到外部介质
spark streaming中维护kafka偏移量到外部介质 以kafka偏移量维护到redis为例. redis存储格式 使用的数据结构为string,其中key为topic:partition, ...
- 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 ...
- Spark Streaming 实现读取Kafka 生产数据
在kafka 目录下执行生产消息命令: ./kafka-console-producer --broker-list nodexx:9092 --topic 201609 在spark bin 目 ...
- Spark Streaming的接收KAFKA的数据
https://github.com/lw-lin/CoolplaySpark/blob/master/Spark%20Streaming%20%E6%BA%90%E7%A0%81%E8%A7%A3% ...
- Flink与Spark Streaming在与kafka结合的区别!
本文主要是想聊聊flink与kafka结合.当然,单纯的介绍flink与kafka的结合呢,比较单调,也没有可对比性,所以的准备顺便帮大家简单回顾一下Spark Streaming与kafka的结合. ...
- Spark Streaming + Flume整合官网文档阅读及运行示例
1,基于Flume的Push模式(Flume-style Push-based Approach) Flume被用于在Flume agents之间推送数据.在这种方式下,Spark Stre ...
- Kafka学习之broker配置(0.8.1版)(转)
broker.id 默认值:无 每一个broker都有一个唯一的id,这是一个非负整数,这个id就是broker的"名字",这样就允许broker迁移到别的机器而不会影响消费者. ...
- Exactly-once Spark Streaming from Apache Kafka
这篇文章我已经看过两遍了.收获颇多,抽个时间翻译下,先贴个原文链接吧.也给自己留个任务 http://blog.cloudera.com/blog/2015/03/exactly-once-spark ...
- Spark官方3 ---------Spark Streaming编程指南(1.5.0)
Design Patterns for using foreachRDD dstream.foreachRDD是一个强大的原语,允许将数据发送到外部系统.然而,了解如何正确有效地使用该原语很重要.避免 ...
随机推荐
- Microsoft CRM-QueryExpression 成员
名称 ColumnSet 获取或设置要包含的列. Criteria 获取或设置过滤查询结果的复杂条件和逻辑过滤器表达式. Distinct 获取或设置查询的结果是否包含重复的实体实例. Entit ...
- 学习React系列(十)——Render Props
解决问题:将行为封装,供多个组件使用(在多个组件之间分享某段代码) 组件中的props属性中包含一个"render"属性(该属性为一个返回值为元素的方法),然后在该组件的rende ...
- 【图解数据结构】 栈&队列
[TOC] 勤于总结,持续输出! 1.栈 1.1栈的定义 栈(stack)是限定在表尾进行插入和删除的操作的线性表. 我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不包 ...
- Docker下ETCD集群搭建
搭建集群之前首先准备两台安装了CentOS 7的主机,并在其上安装好Docker. Master 10.100.97.46 Node 10.100.97.64 ETCD集群搭建有三种方式,分别是Sta ...
- 深入理解.net - 2.多态 Polymorphsim
通过上篇文章 继承的本质 深入介绍了继承过程中对象的的创建过程,相信对继承已经有了一个深入的理解,本文则详细剖析一下面向对象设计的另一要素多态(Polymorphsim). 什么是多态 官方MSDN上 ...
- [SCOI2008]城堡
题目描述 在一个国家里,有n个城市(编号为0 到n-1).这些城市之间有n条双向道 路相连(编号为0 到n-1),其中编号为i的道路连接了城市i和城市ri(一条道 路可以连接一个城市和它自身),长度为 ...
- 【NOIP2017 OFO】
·奇怪的标题可能预示着这一篇博文不是讲算法或者分享题目的吧. [一只情绪化的兔子] 今年的11月12日出奇地比去年温暖.两场比赛结束后的我们在临走前去尝试了OFO共享单车,在成都电子科技大学 ...
- 【NOIP2012-开车旅行】
这道题:你不仅要学会两人交换开车,还要做到高效驾驶. ·分析: 在拨开花哨题目的迷雾之后,发现两个重要突破口: ①从每个点开始,他们的路径是一定的,不存在决策选取. ...
- bzoj 2783: [JLOI2012]树
Description 在这个问题中,给定一个值S和一棵树.在树的每个节点有一个正整数,问有多少条路径的节点总和达到S.路径中节点的深度必须是升序的.假设节点1是根节点,根的深度是0,它的儿子节点的深 ...
- hdu 5533(几何水)
Input The first line contains a integer T indicating the total number of test cases. Each test case ...