spark 连接kafka API 讲解

转自: http://blog.leanote.com/post/kobeliuziyang/Spark2.x%E8%BF%9E%E6%8E%A5Kafka

spark 连接kafka API 各参数详细讲解

  • 一 Spark连接Kafka的两种方式比较
  • 二 0.8,0.10以及更高版本的Kafka
  •     如果spark的批次时间batchTime超过了kafka的心跳时间(30s),需要增加hearbeat.interval.ms以及session.timeout.ms。加入batchTime是5min,那么就需要调整group.max.session.timeout.ms。
  • 2.1 KafkaUtils.creatDirectStream参数
  • 2.1.1 ssc,StreamingContext
  • 2.1.2 locationStrategy: LocationStrategy
  • 2.1.4 kafkaParams
  • 2.2 根据指定offset区间获取RDD
  • 2.3 偏移量操作
  • 三 0.8之前版本 

 

一 Spark连接Kafka的两种方式比较

  •     Kafka consumer传统消费者(老方式)需要连接zookeeper,简称Receiver方式,是高级的消费API,自动更新偏移量,支持WAL,但是效率比较低。
  •     新的方式(高效的方式)不需要连接Zookeeper,但是需要自己维护偏移量,简称直连方式,直接连载broker上,但是需要手动维护偏移量,以迭代器的方式边接收数据边处理,效率较高。    

    consumer的一个group下可以有多个消费者,同时消费但是不会出现重复消费数据的情况。Kafka0.8,0.10只支持直连方式

二 0.8,0.10以及更高版本的Kafka

    直连方式->

和基于Receiver方式相比,这种方式主要有一些几个优点: 

  • 简化并行。我们不需要创建多个 Kafka 输入流,然后 union 他们。而使用 directStream,Spark Streaming 将会创建和 Kafka 分区一样的 RDD 分区个数,而且会从 Kafka 并行地读取数据,也就是说Spark 分区将会和 Kafka 分区有一一对应的关系,这对我们来说很容易理解和使用; 
  • 高效。第一种实现零数据丢失是通过将数据预先保存在 WAL 中,这将会复制一遍数据,这种方式实际上很不高效,因为这导致了数据被拷贝两次:一次是被 Kafka 复制;另一次是写到 WAL 中。但是 Direct API 方法因为没有 Receiver,从而消除了这个问题,所以不需要 WAL 日志; 
  • 恰好一次语义(Exactly-once semantics)。通过使用 Kafka 高层次的 API 把偏移量写入 Zookeeper 中,这是读取 Kafka 中数据的传统方法。虽然这种方法可以保证零数据丢失,但是还是存在一些情况导致数据会丢失,因为在失败情况下通过 Spark Streaming 读取偏移量和 Zookeeper 中存储的偏移量可能不一致。而 Direct API 方法是通过 Kafka 低层次的 API,并没有使用到 Zookeeper,偏移量仅仅被 Spark Streaming 保存在 Checkpoint 中。这就消除了 Spark Streaming 和 Zookeeper 中偏移量的不一致,而且可以保证每个记录仅仅被 Spark Streaming 读取一次,即使是出现故障。但是本方法唯一的坏处就是没有更新 Zookeeper 中的偏移量,所以基于 Zookeeper 的 Kafka 监控工具将会无法显示消费的状况。然而你可以通过 Spark 提供的 API 手动地将偏移量写入到 Zookeeper 中。也没有办法运行监控工具。

    连接:需要添加spark-streaming-kafka-0-10_2.11的包

 
  1.     <dependency>
  2.       <groupId>org.apache.spark</groupId>
  3.       <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
  4.       <version>${spark.version}</version>
  5.     </dependency>

    不需要手动添加kafka-clients等的包,已经包含了相关依赖,不同版本会有不同的兼容性。        

  1. import org.apache.kafka.common.serialization.StringDeserializer
  2. import org.apache.spark.streaming.{Seconds,StreamingContext,kafka010}
  3. import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
  4. import org.apache.spark.streaming.kafka010.KafkaUtils
  5. import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
  6.  
  7.  
  8. val ssc=new StreamingContext("local[4]"),"StatStreamingApp",Seconds(5))
  9. val KafkaParams=Map[String,Object](
  10.     "bootstrap.servers"->"broker1:9092,broker2:9092",
  11.     "key.deserializer"->classOf[StringDeserializer],
  12.     "value.deserilazier"->classOf[StringDeserializer],
  13.     "group.id"->"groupName",
  14.     "auto.offset.reset"->"latest",
  15.     "enable.auto.commit"->(false:java.lang.Boolean)    )
  16. val topics=List("topic1,topic2")
  17. val lines=KafkaUtils.createDirectStream[String,String](
  18.     ssc,PreferConsistent,Subscribe[String,String](topics,kakfaParmas))
  19.     .map(_.value())或.map(_._2)
  20. ssc.start()
  21. ssc.awaitTermination()

    如果spark的批次时间batchTime超过了kafka的心跳时间(30s),需要增加hearbeat.interval.ms以及session.timeout.ms。加入batchTime是5min,那么就需要调整group.max.session.timeout.ms。

    消费者缓存默认为最大64条,如果希望处理超过(64*executor数量)kafka的分区,可以调节spark.streaming.kafka.consumer.cache.maxCapacity 这个参数。另外,可以调节spark.streaming.kafka.consumer.cache.enable=false来禁止缓存,可以解决Spark-19185的bug。

2.1 KafkaUtils.creatDirectStream参数

    需要传入三个参数

2.1.1 ssc,StreamingContext

2.1.2 locationStrategy: LocationStrategy

    表示kafka分区的分配策略,上述的例子中直接传入了org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent,这个类,有四种选项:

  • PreferBrokers:只有executors和kafkaBroker在同一个结点时候才能使用
  • PreferConsistent:一般使用该项,将会一致接收所有分区的数据到executors上。
  • PreferFixed(hostMap:Map[TopicPartition,String]):如果负载不均衡的情况下,可以通过该选项将制定分区的数据分发到制定的主机上。如果map中没有任何指定,那么将会使用PreferConsistent选项。
  • PreferFiexed(hostMap:ju.Map[TopicPartition,String]):与上一条类似,但是传入的是java的Map。ju代表import java.{ util => ju }​, 是java.util.

2.1.3 consumerStrategy: ConsumerStrategy[K, V]​

    表示消费者的一些配置参数,需要使用org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe类来进行包装,一般只需要传入topics和kafkaParams即可,但是也有一些重载方法可以使用。

  • Subscribe[K,V](topics:Iterable[jl.String],kafkaParams:collection.Map[String,Object]):ConsumerStrategy[K,V]
  • Subscribe[K,V](topics:Iterable[String],kafkaParams:collection.Map[String,Object],offsets:collection.Map[TopicPartition,Long]):ConsumerStrategy[K,V]

可以传入一个OffsetMap,指定不同的分区应该从什么位置开始读取数据。默认使用第一个就可以了。

2.1.4 kafkaParams

    这个参数是在consumerStrategy中需要传入的,是一个Map,可以包括的参数有:

链接:http://kafka.apache.org/documentation.html#newconsumerconfigs

其中bootstrap.servers​是必须被设置的

2.2 根据指定offset区间获取RDD

 
  1. import scala.collection.JavaConversions._
  2. import java.{util=>ju}
  3. //OffsetRange(topic名称,partition下标,offset开始下标,offset结束下标)
  4.     val offsetRanges=Array(OffsetRange("kylin_streaming_topic",0,0,100))
  5.     //将scalaMap转为java.util.Map,createRDD只支持javaMap的配置参数
  6.     val juMap:ju.Map[String,Object]=kafkaParams
  7.     val rdd=KafkaUtils.createRDD(ssc.sparkContext,juMap,offsetRanges,PreferConsistent)
  8.     rdd.foreach(println)

    上述代码实现了获取指定topic中第一个partition从0到100offset的数据。并且是以RDD方式接受而不是DStream。

2.3 偏移量操作

2.3.1 获取偏移

 
  1. stream.foreachRDD{rdd=>{
  2.     val offsetRanges=rdd.asInstanceOf[HasOffsetRanges].offsetRanges
  3.     rdd.foreachPartition{iter=>
  4.         val o:OffsetRange=offsetRanges(TaskContext.get.partitionId)
  5.         println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
  6.     }
  7. }

    注意:HashOffsetRanges仅在spark计算链条的开始才能类型转换成功,kafka分区和spark分区一一对应的关系在shuffle之后就会丧失,比如reduceByKey()或者window()。

2.3.2 提交偏移量

    一般情况下,我们都会将enable.auto.commit设置为false,不让spark自动提交偏移量,因为在获取kafka数据之后,我们不能确保数据已经被成功处理并且被没输出或者存储了。可以使用commitAsync来提交偏移量。

 
  1. stream.foreachRDD{rdd=>{
  2.  val offsetRanges=rdd.asInstanceOf[HasOffsetRanges].offsetRanges
  3.  stream.asInstanceOf[CancommitOffsets].commitAsync(offsetRanges)
  4. }
  5. }

2.4 补充

    消费者默认的最大缓存为64k,如果希望处理大于(64*executor)的数量的kafka分区,可以手动配置spark.streaming.kafka.consumer.cache.maxCapacity.

    可以使用spark.streaming.kafka.consumer.cache.enabled=false。

三 0.8之前版本 

    之前的版本支持createDirecStream和createDStream方式,使用KafkaUtils.createDstream来创建dstream,使用receivers来接收数据,利用的是Kafka高层次消费者api,对于所有的receivers接收到的数据将会保存到Spark executors中,然后通过SparkStreaming启动job来处理这些数据,默认可能会出现丢失,可以使用WAL预写日志,将入职存储到HDFS来解决。

 
  1.  val ssc=new StreamingContext("local[4]"),"StatStreamingApp",Seconds(5))
  2.  
  3.  val zkQuorum="master:2181,node1:2181,node2:2181"
  4.  val groupId="groupName"
  5.  val topicMap=Map("topic1"->1,"topic2"->2)
  6.  val lines=KafkaUtils.createStream(ssc,zkQuorum,groupId,topicMap)
  7.  line.map(_._2).count().print()
  8.  
  9.  ssc.start()
  10. ssc.awaitTermination()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值