SparkStreaming和Kafka的整合分为两种方式
第一种是基于Receiver的方式
Receiver的结构:
为了保证并行获取数据,对应每一个外部数据源的分区,所以Receiver也要是分布式的,主要分为三个部分
- Receiver是一个对象,是可以有用户自定义的获取逻辑对象,表示了如何获取数据
- Receiver Tracker是Receiver的协调和调度者,其运行在Driver上
- Receiver Supervisor被Receiver Tracker调度到不同的分布式上运行,其会拿到用户自定义的Receiver对象,使用这个对象来获取外部数据

Receiver的执行过程:
- 在SparkStreaming程序开启的时候,Receiver Tracker使用JobScheduler分发Job到不同的节点,每个Job包含一个Task,这个Task就是Receiver Supervisor,这个部分其实是复用了通用的调度逻辑;
- Receiver Supervisor启动后进行Receiver实例;
- Receiver启动后,就将持续不断地接收外界数据,并持续交给Receiver Supervisor进行数据存储;
- Receiver Supervisor持续不断地接收到Receiver转来地数据,并通过BlockManager来存储数据;
- 获取地数据存储完成后发送元数据给Driver端的Receiver Tracker,包含数据块的id、位置、数量、大小等信息。
特点:操作简单,使用方便
弊端:可能会丢失数据
第二种是基于Direct的方式
Direct的执行过程:

从kafka下面的topic里面拿数据,kafka有几个partition,那么SparkStreaming程序生成的RDD就有几个partition,这是对并行度的一个限制,为了提高并行度,必须repartition
优点:简化了从kafka拉去数据的过程,不需要Receiver;自带容错的,不需要使用预写日志机制,因为拉取数据和处理数据并没有被分割开来(kafka本身也是一个可靠的数据源,不会有丢失数据的风险,如果失败了,可以通过这个偏移量直接从kafka中恢复数据);另外batch任务堆积时,也不会影响数据堆积
Direct的连接方式:
偏移量是由Driver端来维度的;处理偏移量保存到哪里 有两种思路:
- checkpoint,自动保存到checkpoint里面
- 使用zookeeper的API,读zookeeper
SparkStreaming +Kafka集成
Apache Kafka is publish-subscribe messaging rethought as a distributed, partitioned, replicated commit log service. Please read the Kafka documentation thoroughly before starting an integration using Spark.
Apache Kafka将发布-订阅消息传递重新考虑为分布式、分区、复制的提交日志服务。在使用Spark开始集成之前,请仔细阅读Kafka文档。
The Kafka project introduced a new consumer api between versions 0.8 and 0.10, so there are 2 separate corresponding Spark Streaming packages available. Please choose the correct package for your brokers and desired features; note that the 0.8 integration is compatible with later 0.9 and 0.10 brokers, but the 0.10 integration is not compatible with earlier brokers.
Kafka项目在版本0.8和0.10之间引入了一个新的消费者api,因此有两个单独的相应的Spark流包可用。请为您的经纪人选择正确的软件包和所需的功能;注意,0.8集成与后来的0.9和0.10代理兼容,但是0.10集成与以前的代理不兼容。

在工程中需要引入Maven工件 spark-streaming-kafka-0-10 或 spark-streaming-kafka-0-8来使用它。包内提供的KafkaUtils对象可以在StreamingContext和JavaStreamingContext中以你的Kafka消息创建出DStream。由于KafkaUtils可以订阅多个主题,因此它创建出的DStream由成对的主题和消息组成。要创建出一个流数据,需要使用StreamingContext实例、一个由逗号隔开的ZooKeeper主机列表字符串、消费者组的名字(唯一名字),以及一个从主题到针对这个主题的接收器线程数的映射表来调用createStream()方法。
spark-streaming-kafka-0-10版本
导入依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.11.0.2</version>
</dependency>
编写代码
package com.spark.kafka
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.kafka10.KafkaUtils
object KafkaDirect {
def main(args: Array[String]): Unit = {
Logger.getLogger("org").setLevel(Level.ERROR)
// 1.初始化Spark配置信息
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
// 2.初始化SparkStreamingContext
val ssc = new StreamingContext(conf, Seconds(5))
// 3.定义kafka参数
val brokers = "node7-1:9092, node7-2:9092, node7-3:9092, node7-4:9092"
val topic = Set("source")
val consumerGroup = "spark"
// 4.将kafka参数映射为map
val kafkaParam: Map[String, String] = Map(
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer",
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer",
ConsumerConfig.GROUP_ID_CONFIG -> consumerGroup,
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers
)
// 5.通过kafkaUtil创建kafkaDStream
val kafkaDStream = KafkaUtils.createDirectStream(
ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String,String](topics,kafkaParams)
)
// 6.对kafkaDStream做计算(wordCount)
kafkaDStream.map(record => {
record.value().toString
}).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_+_).print()
// 7.启动SparkStreaming
ssc.start()
ssc.awaitTermination()
}
}
spark-streaming-kafka-0-8版本
导入依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.11.0.2</version>
</dependency>
编写代码
package com.spark.kafka
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.dstream.ReceiverInputDStream
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* 通过SparkStreaming从Kafka读取数据,并将读取过来的数据做简单计算,最终打印到控制台
*/
object KafkaReceiver {
def main(args: Array[String]): Unit = {
Logger.getLogger("org").setLevel(Level.ERROR)
// 1.初始化Spark配置信息
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
// 2.初始化SparkStreamingContext
val ssc = new StreamingContext(conf, Seconds(5))
// 3.定义kafka参数
val brokers = "node7-1:9092, node7-2:9092, node7-3:9092, node7-4:9092"
val topic = Map(("source", 1))
val consumerGroup = "spark"
// 4.将kafka参数映射为map
val kafkaParam: Map[String, String] = Map[String, String](
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer",
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer",
ConsumerConfig.GROUP_ID_CONFIG -> consumerGroup,
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers
)
// 5.通过kafkaUtil创建kafkaDStream
val kafkaDStream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, kafkaParam, topic, StorageLevel.MEMORY_ONLY)
// 6.对kafkaDStream做计算(wordCount)
kafkaDStream.foreachRDD(rdd => {
val word: RDD[String] = rdd.flatMap(_._2.split(" "))
val wordAndOne: RDD[(String, Int)] = word.map((_, 1))
val wordAndCount: RDD[(String, Int)] = wordAndOne.reduceByKey(_+_)
wordAndCount.collect().foreach(println)
})
// 7.启动SparkStreaming
ssc.start()
ssc.awaitTermination()
}
}
本文介绍了Spark Streaming与Kafka的两种整合方式:基于Receiver和Direct。基于Receiver的方式操作简单但可能丢失数据;Direct方式简化了数据拉取过程,自带容错机制,适合高并发场景。文中还详细介绍了如何在Spark项目中引入Maven依赖并实现代码示例。
364

被折叠的 条评论
为什么被折叠?



