Spark Streaming 集成 Kafka 详解

本文详细介绍了Spark Streaming从Kafka数据源进行数据摄取的两种方式——Receiver-based和Direct Approach,分析了各自的优缺点。Receiver-based方式通过Receiver持续读取数据,可能面临数据丢失风险,而Direct Approach避免了Receiver,提供更高效的零数据丢失解决方案。文章还探讨了Kafka offsets管理,包括Spark Streaming checkpoints、HBase和Zookeeper存储方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Spark Streaming 支持多种输入源数据的读取,其中基本数据源有:File System、Socket connections;而高级数据源有:Kafka、Flume、Kinesis等。但是高级数据源需要额外依赖,而且不能在 Spark Shell 中测试这些高级数据源,如果想要在Spark Shell 中测试需要下载依赖到Spark 依赖库中。

关于读取Kafka 的方式,Spark Streaming 官方提供了两种方式:Receiver 和 Direct,此两种读取方式存在很大的不同,当然也各有优劣,接下来就让我们具体刨解这两种数据读取方式。此外,因为Kafka 在0.8 和0.10 之间引入了一个新的消费者API,因此有两个独立的Spark Streaming 包可用,根据功能来选择合适的包,0.8 版本兼容后来的0.9 和0.10,但是0.10 不向前兼容。官网地址:http://spark.apache.org/docs/2.1.2/streaming-kafka-integration.html

 

Receiver-based Approach

Spark 官方最先提供了基于 Receiver 的Kafka 数据消费模式。但会存在程序失败丢失数据的风险,在Spark 1.2 时引入了一个配置参数 spark.streaming.receiver.writeAheadLog.enable (WAL)以规避此风险。

 

Receiver-based 读取方式

Receiver-based 的Kafka 读取方式是基于Kafka 高阶API 来实现对Kafka 数据的消费。在提交Spark Streaming 任务后,Spark 集群会划出指定的Receivers 来专门、持续不断、异步读取Kafka 数据,读取时间间隔以及每次去读offsets 范围可以由参数来配置。读取的数据保存在Receiver 中,具体StorageLevel 方式由用户指定,入MEMORY_ONLY等。当driver 触发Batch 任务的时候,Receivers 中的数据会转移到剩余的Executors 中去执行。在执行完后,Receivers 会相应更新Zookeeper 的offsets。如要确保at least once 的读取方式,可以设置spark.streaming.receiver.writeAheadLog.enable为true。集体Receiver 执行流程如下图:

 

Receiver-based 读取实现

Kafka 的high-level 数据读取方式让用户可以专注于所读数据,而不用关注或维护consumer 的offsets,这减少用户的工作量以及代码量而且相对比较简单。因此,在刚开始引入Spark Streaming 计算引擎时,我们优先考虑采用此种方式来读取数据,具体代码如下:

# 添加依赖

groupId = org.apache.spark
artifactId = spark-streaming-kafka-0-8_2.11
version = 2.1.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]) 

# 注意
1. Kafka中的主题分区与Spark Streaming中生成的RDD分区无关。因此,增加KafkaUtils.createStream()中特定于主题的分区的数量只会增加使用单个接收器中使用的主题的线程数。它不会增加Spark在处理数据时的并行性。
2. 可以使用不同的组和主题创建多个Kafka输入DStream,以使用多个接收器并行接收数据。
3. 如果开启了WAL,可以使用KafkaUtils.createStream(..., StorageLevel.MEMORY_AND_DISK_SER)

# 部署
对于Scala和Java应用程序,如果您使用SBT或Maven进行项目管理,则将spark-streaming-kafka-0-8_2.11及其依赖项打包到应用程序JAR中。确保spark-core_2.11和spark-streaming_2.11被标记为provider的依赖项,因为它们已存在于Spark安装中。然后使用spark-submit启动您的应用程序.
./bin/spark-submit --packages org.apache.spark:spark-streaming-kafka-0-8_2.11:2.1.2 ...
或者
you can also download the JAR of the Maven artifact spark-streaming-kafka-0-8-assembly from the Maven repository and add it to spark-submit with --jars.    
 
# 参考资料
官方地址:http://spark.apache.org/docs/2.1.2/streaming-kafka-0-8-integration.html

 

Receiver-based 读取问题

为了防数据丢失,我们做了checkpoint 操作以及配置了spark.streaming.receiver.writeAheadLog.enable 参数,提高了receiver 的吞吐量。采用MEMORY_AND_DISK_SER 方式读取数据、提高单Receiver 的内存或事调大并行度,将数据分散到多个Receiver 中去,但是也是会出现各种情况的问题:

  • 配置spark.streaming.receiver.writeAheadLog.enable参数,每次处理之前需要将该batch内的日志备份到checkpoint目录中,这降低了数据处理效率,反过来又加重了Receiver端的压力;另外由于数据备份机制,会受到负载影响,负载一高就会出现延迟的风险,导致应用崩溃。
  • 采用MEMORY_AND_DISK_SER降低对内存的要求。但是在一定程度上影响计算的速度。
  • 单Receiver内存。由于receiver也是属于Executor的一部分,那么为了提高吞吐量,提高Receiver的内存。但是在每次batch计算中,参与计算的batch并不会使用到这么多的内存,导致资源严重浪费。
  • 提高并行度,采用多个Receiver来保存Kafka的数据。Receiver读取数据是异步的,并不参与计算。如果开较高的并行度来平衡吞吐量很不划算。
  • Receiver和计算的Executor的异步的,那么遇到网络等因素原因,导致计算出现延迟,计算队列一直在增加,而Receiver则在一直接收数据,这非常容易导致程序崩溃。
  • 在程序失败恢复时,有可能出现数据部分落地,但是程序失败,未更新offsets的情况,这导致数据重复消费。

 

Direct Approach(No Receivers)

区别与Receiver-based 的数据消费方法,Spark 官方在 1.3 时引入了Direct 方式的Kafka 消费方式。相对于Receiver-based 的方法,Direct 方式具有一下的优势:

  • 简化并行性
    • 无需创建多个输入Kafka流并将它们联合起来。使用directStream,Spark Streaming将创建与要使用的Kafka分区一样多的RDD分区,这些分区将并行地从Kafka读取数据。因此,Kafka和RDD分区之间存在一对一的映射,这更容易理解和调整。
  • 高效
    • 在第一种方法中实现零数据丢失需要将数据存储在Write Ahead Log中,这进一步复制了数据。这实际上是低效的,因为数据有效地被复制两次 - 一次由Kafka复制,第二次由Write Ahead Log复制。第二种方法消除了问题,因为没有接收器,因此不需要Write Ahead Logs。只要您有足够的Kafka保留,就可以从Kafka恢复消息。
  • 强一致性
    • 第一种方法使用Kafka的高级API在Zookeeper中存储消耗的偏移量。传统上,这是从Kafka使用数据的方式。虽然这种方法(与预写日志结合使用)可以确保零数据丢失(即至少一次语义),但某些记录在某些故障下可能会被消耗两次的可能性很小。这是因为Spark Streaming可靠接收的数据与Zookeeper跟踪的偏移之间存在不一致。因此,在第二种方法中,我们使用不使用Zookeeper的简单Kafka API。 Spark Streaming在其检查点内跟踪偏移量。这消除了Spark Streaming和Zookeeper / Kafka之间的不一致,因此尽管出现故障,Spark Streaming也会有效地接收每条记录一次。为了实现输出结果的一次性语义,将数据保存到外部数据存储的输出操作必须是幂等的,或者是保存结果和偏移的原子事务。

 

Direct 读取方式

Direct 方式采用Kafka 简单的 consumer API 方式来读取数据,无需经由Zookeeper,此种方式不再需要专门的Receiver 来持续不断的读取数据。当Batch 任务触发时,由Executor 读取数据,并参与到其他的Executor 的数据计算过程中去。driver 来决定读取多少offsets,并将offsets 交由checkpoint 来维护。当触发下次Batch 任务时,再由executor 读取Kafka 数据并计算。从此过程我们可以发现Direct 方式无需Receiver 读取数据,而是需要计算时在读取数据,所以Direct 方式的数据消费对内存的要求不高,只需要考虑批量计算所需要的内存即可;另外Batch 任务堆积时,也不会影响数据堆积。其具体读取方式如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值