SparkStreaming + Kafka集成指南(Kafka版本要求0.8.2.1以上)

注意:spark2.3版本已经取消了对kafka0.8版本的支持 
  这里主要对如何配置SparkStreaming程序接受kafka的数据进行介绍。目前有两种方式:较老的方式是通过使用Receivers和Kafka的高阶API,新的方式(从spark1.3版本开始)不在使用Receivers。这是两种不同的编程模型、有不同的性能特征和不同的语义,更多细节求阅读下文。目前两种方式都能稳定的接入当前版本的spark。 
方式一:基于Receiver的方式
    这种方式使用Receiver来接受数据。Receiver是使用Kafka的高阶消费者API实现的。对于所有的receiver而要,通过receiver接收来自于kafka的数据都会被存储在Spark的executor中,然后Spark Streaming会发布job来处理数据。 
  然而,在默认配置下这种方式在失败的情况下会丢失数据(参考[receiver的可靠性][1]部分来确保零数据丢失,你需要额外的SparkStreaming程序中使用WAL机制(Spark1.2版本中开始加入)
Receiver的可靠性
基于可靠性作区分有两种数据源。一种数据源类似于Kafka和Flume,允许对接收到的数据做确认。如果系统从这些可靠的数据源正确接收到了数据,就可以确保没有数据由于各种原因造成失败而被丢失。这导致有两种receivers:  
可靠的receiver-当数据被正确的接受并被保存到Spark中备份时,receiver才会发送正确的确认消息到可靠的数据源  
不可靠的receiver-不可靠的receiver不会向数据源发送确认消息。这可以应用于不支持确认消息的数据源,或者在某些不需要确认或者避免确认的情况下应用到可靠数据源上。  
具体如何编码一个可靠的数据源参考自定义Receiver指南

         接下来我们讨论在流处理应用中,怎样使用这种方式。 
1.Linking :对于Scala或者Java的应用通过使用SBT或者Maven将下面的依赖加入你的流处理应用

groupId = org.apache.spark  
artifactId = spark-streaming-kafka-0.8_2.11  
version = 2.3.0

        对于Python的应用来说,在你部署你的应用时,你需要添加上述库和他的依赖,更多的细节参看部署模块。 
2.编码:对于流处理应用的代码,引入KafkaUtils包,并且如下代码创建输入的DStream.(以Scala代码为例)

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的类及他们的解码类,通过使用createStream方法中的参数,具体参看 API 文档
        要点:
     ·Kafka中对Topic的分区与SparkStreaming程序中分区生成RDD没有关联。因此增大KafkaUtils.createStream()方法中topic的指定分区数参数仅仅只会增加单个receiver中消费topic的线程数。这不会增加Spark程序处理数据的并如果行度,参阅主要文档获取更多信息。 
       ·为了并行的接收数据可以使用多个Receiver,来创建来自不同组和不同Topic的Kafka输入流(Input DStream) 
    ·如果你利用了一个备份的文件系统如hdfs启动了WAL(WriteAheadLog)功能,接收到的数据会被写入到日志中,因此,需要将输入数据流的存储级别(StorageLevel)设置为StorageLevel.MEMORY_AND_DISK_SER(即,KafkaUtils.createStream(...,StorageLevel.MEMORY_AND_DISK_SER)) 
    3.部署:对于Spark应用来说,Spark-submit被用来部署你的应用。然而,Scala/JAVA应用程序的部署和Python应用的部署有些许不同。 
    对于Scala和JAVA应用,如果你是用SBT或者MAVEN来对项目进行管理,那么spark-streaming-kafka-0-8_2.11及其依赖包需要被添加到应用程序的jar包中。确保spark-core_2.11及spark-streaming_2.11在你的应用中被标记为provided级别的依赖,这些已经存在于Spark中。然后使用spark-submit命令来部署你的应用,(查阅主文档中 部署部分获取指南)
     对于缺少SBT和MAVEN的python应用来说,spark-streaming-kafka-0-8_2.11及其依赖包需要通过使用--packages参数添加的spark-submit命令中(参考 应用提交指南)
./bin/spark-submit --packages org.apache.spark:spark-streaming-kafka-0-8_2.11:2.3.0 ...
        另一种方法,你也可以通过MAVEN下载spark-streaming-kafka-0-8-assembly然后通过--jars参数提交。
方式二:直接连接(不通过Receiver)
     该种新的没有Receiver的“直接”方式在Spark1.3中才被加入,确保两端的Spark版本都符合要求。与使用Receiver接收数据不同的是,该种方式周期性的查询每一个分区最后的偏移量,并且定义每批处理的数据范围。当处理数据的作业启动时,通常使用kafka简单的消费者的API(类似于从文件系统读取文件的偏移量)。值得注意的是这种特性在Spark1.3版本之后才提供了Scala和JAVA版本的API,在1.4版本之后才提供了python版本的API。 
      这种方法相较于基于receiver的方法有如下优点: 
·简化并行:不需要创建多个Kafka的输入流并将它们结合起来。通过使用DirectStream,SparkStreaming将会创建多个RDD分区,并行的从kafka的分区中消费数据。因此在Kafka和RDD分区之间有一个一对一的映射,这很容易理解和调整。  
·效率:在第一种方式中想要实现数据的0丢失,需要将数据写入WAL,增加了数据的冗余性。因为数据复制了两次,一次在WAL,一次在Kafka,降低了效率。第二种方式解决了这个问题,因为没有Receiver,因此不需要使用WAL,只要Kafka中保存了消息,消息就可以从Kafka中恢复。   
·只读一次语义:第一种方式通过使用Kafka的高阶API在zookeeper中保存消费者的偏移量。这是消费Kafka中的数据的传统方式。然而这种方式(与WAL相结合的方式)能够确保数据的0丢失(即至少一次语义),但是很小的概率在失败的时候有的消息会被消费两次。这是由于数据是在SparkStreaming程序中被可靠的接收的,而偏移量是在Zookeeper中被保存的。因此在第二种方式中,我们通常使用简单Kafka的API而不使用Zookeeper.SparkStreaming通过使用checkpoints来跟踪偏移量。这消除了SparkStreaming和Zookeeper/Kafka之间的矛盾,尽管会发生失败每条记录也只会被SparkStreaming有效的消费一次。为了实现精确地输出一次的语义,将数据保存到外部数据存储的操作必须是幂等的,或者是保存结果和偏移量的原子操作(参考主文档中 输出算子的语义获得更多信息)
    注意,该种方式的缺点在于没有在Zookeeper中更新偏移量,因此基于Zookeeper的Kafka监控工具不会跟新。然而,你可以在每个batch中访问该方法处理的偏移量,并更新到zookeeper中。 
       接下里我们将讨论如何在你的streaming应用里使用这种方式。 
1.Linking:这种方式仅支持Scala和Java的应用。在你的sbt或maven项目中加入如下引用(具体可参考主文档中 引用部分
    
groupId = org.apache.spark
    artifactId = spark-streaming-kafka-0-8_2.11
    version = 2.3.0

2.编码:在流式应用编码中,引入KafkaUtils类并如如下代码一样创建一个输入的DStream
Scala:
import org.apache.spark.streaming.kafka._
val directKafkaStream = KafkaUtils.createDirectStream[
    [key class], [value class], [key decoder calss], [value decoder class]](
    streamingContext, [map of Kafka parameter], [set if topics to consume])

Java:
import org.apache.spark.streaming.kafka.*;
 JavaPairInputDStream<String, String> directKafkaStream =
     KafkaUtils.createDirectStream(streamingContext,
         [key class], [value class], [key decoder class], [value decoder class],
         [map of Kafka parameters], [set of topics to consume]);
 
       同样可以传入一个messageHandler参数到createDirectStream方法中,获取包含当前消息元数据的KafkaMessageAnd-
-Metadata,并将其转化为任何你想要的类型。详见 API文档
    在Kafka的参数中,你必须制定metadata.broker.list参数或bootstrap.servers参数。Kafka会默认从每一个分区的最大偏移量开始消费消息,如果你指定auto.offset.reset参数为smallest,那么就会从最小偏移量处开始消费。
   你同样可以通过使用KafkaUtils.createDirectStream方法从任意位置开始消费数据。此外,如果你想要获取每个batch中kafka消费的偏移量你可以进行如下操作。
Scala:
// Hold a reference to the current offset ranges, so it can be used downstream
 var offsetRanges = Array.empty[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}")
   }
   ...
 }

 
JAVA:
 // Hold a reference to the current offset ranges, so it can be used downstream
 var offsetRanges = Array.empty[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}")
   }
   ...
 }

       如果你想使用一些基于Zookeeper的kafka监控工具来监控流处理应用进程的话你可以使用这种方式去更新zookeeper。
   注意,对HashOffsetRanges的类型转换只有在第一个方法而不是在其随后的一列中方法中调用了directKafkaStream才会成功。你可以使用transform方法来替换foreachRDD()方法来作为获取偏移量的第一个方法,然后使用更多的Spark方法。然而,RDD分区和Kafka分区之间一对一的映射关系不在任何shuffle或热partition方法之后仍继续存在,如reduceByKey()方法或window()方法。
   另一件需要注意的事情是这种方式没有使用Receivers,标准的与receiver相关的(如配置中的spark.streaming.receiver.\*)不能应用到以这种方式创建的输入DStream中(适用其他输入DStreams),但是尅使用spark.streaming.kafka.\*的配置。spark.streaming.kafka.maxRatePerPartition参数是一个重要的配置,这决定了每个kafka分区将被这种直接的API所读取的最大速率(每秒的条数)。 
 3.部署:与第一种方式相同。
 
原文链接:http://spark.apache.org/docs/latest/streaming-kafka-0-8-integration.html#approach-2-direct-approach-no-receivers

猜你喜欢

转载自blog.csdn.net/v_gbird/article/details/80323602
今日推荐