这篇博客是基于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. win10被微软流氓更新后编译基于visual Studio的web项目报[ArgumentOutOfRangeException: 指定的参数已超出有效值的范围

    最近忙得算焦头烂额.就在这个时候.一个不留神.微软的自动更新打开了.这流氓就在我百忙之中强迫我休息了一个多小时. 焦急等待它更新完以后赶紧打开visual studio跑代码.运行好几次都报错.想想不 ...

  2. 远程连接服务器jupyter notebook、浏览器以及深度学习可视化方法

    h1 { counter-reset: h2counter; } h2 { counter-reset: h3counter; } h3 { counter-reset: h4counter; } h ...

  3. js在光标处插入内容

    //场景一 简易的页面可以这样写var range = window.getSelection().getRangeAt(0);range.insertNode(document.createText ...

  4. 用redis的订阅发布解决了扫码支付实时响应的问题

    一.场景描述: PC收银台的浏览器展示了收款二维码,用户扫了支付二维码,支付完成后,浏览器需要实时响应支付结果. 二.问题描述: 扫码支付的支付结果一般通过服务端回调和主动查询来获取,显示二维码之后, ...

  5. 计蒜客NOIP2017提高组模拟赛(五)day2-蚂蚁搬家

    传送门 这题可以用线段树来维护 #include<cstdio> #include<cstdlib> #include<algorithm> #include< ...

  6. poj 2528 (线段树+离散化)

    poj 2528 For each input data set print the number of visible posters after all the posters are place ...

  7. ZOJ 3228 Searching the String(AC自动机)

    Searching the String Time Limit: 7 Seconds      Memory Limit: 129872 KB Little jay really hates to d ...

  8. vue-cli2.9.1如何自动打开浏览器及实现手机调试

    在vue-cli2.9.1以前我们运行 "npm run dev" 程序会自动打开浏览器进行调试,而且在手机浏览器输入 "IP地址:8080" 能实现在手机端的 ...

  9. c# txt文件的读取和写入

    我们在工程实践中经常要处理传感器采集的数据,有时候要把这些数据记录下来,有时候也需要把记录下来的数据读取到项目中.接下来我们用C#演示如何对txt文件进行读写操作.我们要用到StreamReader  ...

  10. C语言程序第一次作业

    (一)实验总结 1. 求圆面积和周长 (1)题目 输入圆的半径,计算圆的周长和面积. (2)流程图 (3)测试数据及运行结果 测试数据1:r=2 运行结果: (4)实验分析 没有问题 2.判断闰年 ( ...