生产中,为了保证kafka的offset的安全性,并且防止丢失数据现象,会手动维护偏移量(offset)

版本:kafka:0.8

其中需要注意的点:

1:获取zookeeper记录的分区偏移量

2:获取broker中实际的最小和最大偏移量

3:将实际的偏移量和zookeeper记录的偏移量进行对比,如果zookeeper中记录的偏移量在实际的偏移量范围内则使用zookeeper中的偏移量
4:反之,使用实际的broker中的最小偏移量

KafkaHelper:

  1. import kafka.common.TopicAndPartition
  2. import kafka.message.MessageAndMetadata
  3. import kafka.serializer.StringDecoder
  4. import kafka.utils.{ZKGroupTopicDirs, ZkUtils}
  5. import org.I0Itec.zkclient.ZkClient
  6. import org.apache.spark.SparkException
  7. import org.apache.spark.streaming.StreamingContext
  8. import org.apache.spark.streaming.dstream.InputDStream
  9. import org.apache.spark.streaming.kafka.{KafkaCluster, KafkaUtils, OffsetRange}
  10. import org.apache.spark.streaming.kafka.KafkaCluster.Err
  11.  
  12. /**
  13. * KafkaHelper类提供两个共有方法,一个用来创建direct方式的DStream,另一个用来更新zookeeper中的消费偏移量
  14. * @param kafkaPrams kafka配置参数
  15. * @param zkQuorum zookeeper列表
  16. * @param group 消费组
  17. * @param topic 消费主题
  18. */
  19. class KafkaHelper(kafkaPrams:Map[String,String],zkQuorum:String,group:String,topic:String) extends Serializable{
  20.  
  21. private val kc = new KafkaCluster(kafkaPrams)
  22. private val zkClient = new ZkClient(zkQuorum)
  23. private val topics = Set(topic)
  24.  
  25. /**
  26. * 获取消费组group下的主题topic在zookeeper中的保存路径
  27. * @return
  28. */
  29. private def getZkPath():String={
  30. val topicDirs = new ZKGroupTopicDirs(group,topic)
  31. val zkPath = topicDirs.consumerOffsetDir
  32. zkPath
  33. }
  34.  
  35. /**
  36. * 获取偏移量信息
  37. * @param children 分区数
  38. * @param zkPath zookeeper中的topic信息的路径
  39. * @param earlistLeaderOffsets broker中的实际最小偏移量
  40. * @param latestLeaderOffsets broker中的实际最大偏移量
  41. * @return
  42. */
  43. private def getOffsets(children:Int,zkPath:String,earlistLeaderOffsets:Map[TopicAndPartition, KafkaCluster.LeaderOffset],latestLeaderOffsets: Map[TopicAndPartition, KafkaCluster.LeaderOffset]): Map[TopicAndPartition, Long] = {
  44. var fromOffsets: Map[TopicAndPartition, Long] = Map()
  45. for(i <- 0 until children){
  46. //获取zookeeper记录的分区偏移量
  47. val zkOffset = zkClient.readData[String](s"${zkPath}/${i}").toLong
  48. val tp = TopicAndPartition(topic,i)
  49. //获取broker中实际的最小和最大偏移量
  50. val earlistOffset: Long = earlistLeaderOffsets(tp).offset
  51. val latestOffset: Long = latestLeaderOffsets(tp).offset
  52. //将实际的偏移量和zookeeper记录的偏移量进行对比,如果zookeeper中记录的偏移量在实际的偏移量范围内则使用zookeeper中的偏移量,
  53. //反之,使用实际的broker中的最小偏移量
  54. if(zkOffset>=earlistOffset && zkOffset<=latestOffset) {
  55. fromOffsets += (tp -> zkOffset)
  56. }else{
  57. fromOffsets += (tp -> earlistOffset)
  58. }
  59. }
  60. fromOffsets
  61. }
  62.  
  63. /**
  64. * 创建DStream
  65. * @param ssc
  66. * @return
  67. */
  68. def createDirectStream(ssc:StreamingContext):InputDStream[(String, String)]={
  69. //----------------------获取broker中实际偏移量---------------------------------------------
  70. val partitionsE: Either[Err, Set[TopicAndPartition]] = kc.getPartitions(topics)
  71. if(partitionsE.isLeft)
  72. throw new SparkException("get kafka partitions failed:")
  73. val partitions = partitionsE.right.get
  74. val earlistLeaderOffsetsE: Either[Err, Map[TopicAndPartition, KafkaCluster.LeaderOffset]] = kc.getEarliestLeaderOffsets(partitions)
  75. if(earlistLeaderOffsetsE.isLeft)
  76. throw new SparkException("get kafka earlistLeaderOffsets failed:")
  77. val earlistLeaderOffsets: Map[TopicAndPartition, KafkaCluster.LeaderOffset] = earlistLeaderOffsetsE.right.get
  78. val latestLeaderOffsetsE: Either[Err, Map[TopicAndPartition, KafkaCluster.LeaderOffset]] = kc.getLatestLeaderOffsets(partitions)
  79. if(latestLeaderOffsetsE.isLeft)
  80. throw new SparkException("get kafka latestLeaderOffsets failed:")
  81. val latestLeaderOffsets: Map[TopicAndPartition, KafkaCluster.LeaderOffset] = latestLeaderOffsetsE.right.get
  82. //----------------------创建kafkaStream----------------------------------------------------
  83. var kafkaStream:InputDStream[(String, String)]=null
  84. val zkPath: String = getZkPath()
  85. val children = zkClient.countChildren(zkPath)
  86. //根据zookeeper中是否有偏移量数据判断有没有消费过kafka中的数据
  87. if(children > 0){
  88. val fromOffsets:Map[TopicAndPartition, Long] = getOffsets(children,zkPath,earlistLeaderOffsets,latestLeaderOffsets)
  89. val messageHandler = (mmd: MessageAndMetadata[String, String]) => (mmd.topic, mmd.message())
  90. //如果消费过,根据偏移量创建Stream
  91. kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder, (String, String)](
  92. ssc, kafkaPrams, fromOffsets, messageHandler)
  93. }else{
  94. //如果没有消费过,根据kafkaPrams配置信息从最早的数据开始创建Stream
  95. kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaPrams, topics)
  96. }
  97. kafkaStream
  98. }
  99.  
  100. /**
  101. * 更新zookeeper中的偏移量
  102. * @param offsetRanges
  103. */
  104. def updateZkOffsets(offsetRanges:Array[OffsetRange])={
  105. val zkPath: String = getZkPath()
  106. for( o <- offsetRanges){
  107. val newZkPath = s"${zkPath}/${o.partition}"
  108. //将该 partition 的 offset 保存到 zookeeper
  109. ZkUtils.updatePersistentPath(zkClient, newZkPath, o.fromOffset.toString)
  110. }
  111. }
  112. }

  

驱动类:

  1. package CC
  2.  
  3. import org.apache.spark.SparkConf
  4. import org.apache.spark.sparkStreaming.kafka.KafkaHelper
  5. import org.apache.spark.streaming.dstream.InputDStream
  6. import org.apache.spark.streaming.kafka.{HasOffsetRanges, OffsetRange}
  7. import org.apache.spark.streaming.{Seconds, StreamingContext}
  8.  
  9. /**
  10. * Created by angel;
  11. */
  12. object TestKafkaHelper {
  13. def main(args: Array[String]): Unit = {
  14.  
  15. val Array(timeInterval,brokerList,zkQuorum,topic,group) = Array(
  16. "2"
  17. , "hadoop01:9092,hadoop02:9092,hadoop03:9092"
  18. , "hadoop01:2181,hadoop02:2181,hadoop03:2181"
  19. , "CustomerContacts"
  20. , "CustomerContacts"
  21. )
  22.  
  23. val conf = new SparkConf().setAppName("KafkaDirectStream").setMaster("local[2]")
  24. val ssc = new StreamingContext(conf,Seconds(timeInterval.toInt))
  25.  
  26. //kafka配置参数
  27. val kafkaParams = Map(
  28. "metadata.broker.list" -> brokerList,
  29. "group.id" -> group,
  30. "auto.offset.reset" -> kafka.api.OffsetRequest.SmallestTimeString
  31. )
  32.  
  33. val kafkaHelper = new KafkaHelper(kafkaParams,zkQuorum,topic,group)
  34.  
  35. val kafkaStream: InputDStream[(String, String)] = kafkaHelper.createDirectStream(ssc)
  36.  
  37. var offsetRanges = Array[OffsetRange]()
  38.  
  39. kafkaStream.transform( rdd =>{
  40. offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
  41. rdd
  42. }).map( msg => msg._2)
  43. .foreachRDD( rdd => {
  44. rdd.foreachPartition( partition =>{
  45. partition.foreach( record =>{
  46. //处理数据的方法
  47. println(record)
  48. })
  49. })
  50. kafkaHelper.updateZkOffsets(offsetRanges)
  51. })
  52.  
  53. ssc.start()
  54. ssc.awaitTermination()
  55. ssc.stop()
  56. }
  57.  
  58. }

  

sparkStreaming消费kafka-0.8方式:direct方式(存储offset到zookeeper)的更多相关文章

  1. SparkStreaming获取kafka数据的两种方式:Receiver与Direct

    简介: Spark-Streaming获取kafka数据的两种方式-Receiver与Direct的方式,可以简单理解成: Receiver方式是通过zookeeper来连接kafka队列, Dire ...

  2. SparkStreaming与Kafka,SparkStreaming接收Kafka数据的两种方式

    SparkStreaming接收Kafka数据的两种方式 SparkStreaming接收数据原理 一.SparkStreaming + Kafka Receiver模式 二.SparkStreami ...

  3. sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)-- 2

    参考上篇博文:https://www.cnblogs.com/niutao/p/10547718.html 同样的逻辑,不同的封装 package offsetInZookeeper /** * Cr ...

  4. sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)

    版本声明: kafka:1.0.1 spark:2.1.0 注意:在使用过程中可能会出现servlet版本不兼容的问题,因此在导入maven的pom文件的时候,需要做适当的排除操作 <?xml ...

  5. SparkStreaming消费kafka中数据的方式

    有两种:Direct直连方式.Receiver方式 1.Receiver方式: 使用kafka高层次的consumer API来实现,receiver从kafka中获取的数据都保存在spark exc ...

  6. Spark-Streaming获取kafka数据的两种方式:Receiver与Direct的方式

    简单理解为:Receiver方式是通过zookeeper来连接kafka队列,Direct方式是直接连接到kafka的节点上获取数据 Receiver 使用Kafka的高层次Consumer API来 ...

  7. 解析SparkStreaming和Kafka集成的两种方式

    spark streaming是基于微批处理的流式计算引擎,通常是利用spark core或者spark core与spark sql一起来处理数据.在企业实时处理架构中,通常将spark strea ...

  8. 工具篇-Spark-Streaming获取kafka数据的两种方式(转载)

    转载自:https://blog.csdn.net/weixin_41615494/article/details/7952173 一.基于Receiver的方式 原理 Receiver从Kafka中 ...

  9. spark-streaming获取kafka数据的两种方式

    简单理解为:Receiver方式是通过zookeeper来连接kafka队列,Direct方式是直接连接到kafka的节点上获取数据 一.Receiver方式: 使用kafka的高层次Consumer ...

随机推荐

  1. linux dynamic lib

    // test1.h ; struct AA { int a,b: }; AA b(5,6); int ball(); // test1.cpp # include"test1.h" ...

  2. 构造函数中base与this的区别

    base是对父类的引用,而this是对类本身的引用. namespace ConsoleApplication1 { public class BaseClass { private string n ...

  3. WinCE平台的程序编译到Win32平台下运行

    最近做的项目中,有一个在WinCE平台上跑的程序,后来随着项目的发展,要求此程序在PC上也能跑.感谢VS 2005提供的多平台支持,只需要几分钟就可以解决这个问题,方法很简单,下面是我处理的过程. 1 ...

  4. push to origin/master was rejected错误解决方案

    idea中,发布项目到OSChina的Git中,当时按照这样的流程添加Git,然后push,提示:push to origin/master war rejected". 解决方案如下: 1 ...

  5. hbase搭建

    0. 软件版本下载 http://mirror.bit.edu.cn/apache/hbase/   1. 集群环境 Master 172.16.11.97 Slave1 172.16.11.98 S ...

  6. Exception类的学习与继承总结

    日期:2018.11.11 星期日 博客期:023 Exception类的学习与继承总结 说起来我们上课还是说过的!老师提到了报错问题出现主要分Exception和Error两类!第一次遇见这个问题是 ...

  7. shell中的ps命令详解

    ps简介:Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的 ...

  8. hdu4966 最小树形图+虚根

    /* 辛辛苦苦调试半天, 过了样例,竟然没有ac!! 网上对比了ac代码,感觉添加一个虚根就能ac 但是想不明白为什么 */ /* 第二天想了下,知道了为什么wa:因为从等级0连到其他课程等级i的不止 ...

  9. Linux系统下inode满了导致无法写文件的解决思路

    解决思路1:删除无用的临时文件,释放inode 进入/tmp目录,执行find -exec命令 find  /tmp  -type  f  -exec  rm  {}  \; 遍历寻找0字节的文件,并 ...

  10. 使用springboot actuator监控应用

    微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的处理和传递,出现了异常如何快速定位是哪个环节出现了问题? ...