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

版本:kafka:0.8

其中需要注意的点:

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

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

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

KafkaHelper:

import kafka.common.TopicAndPartition
import kafka.message.MessageAndMetadata
import kafka.serializer.StringDecoder
import kafka.utils.{ZKGroupTopicDirs, ZkUtils}
import org.I0Itec.zkclient.ZkClient
import org.apache.spark.SparkException
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka.{KafkaCluster, KafkaUtils, OffsetRange}
import org.apache.spark.streaming.kafka.KafkaCluster.Err /**
* KafkaHelper类提供两个共有方法,一个用来创建direct方式的DStream,另一个用来更新zookeeper中的消费偏移量
* @param kafkaPrams kafka配置参数
* @param zkQuorum zookeeper列表
* @param group 消费组
* @param topic 消费主题
*/
class KafkaHelper(kafkaPrams:Map[String,String],zkQuorum:String,group:String,topic:String) extends Serializable{ private val kc = new KafkaCluster(kafkaPrams)
private val zkClient = new ZkClient(zkQuorum)
private val topics = Set(topic) /**
* 获取消费组group下的主题topic在zookeeper中的保存路径
* @return
*/
private def getZkPath():String={
val topicDirs = new ZKGroupTopicDirs(group,topic)
val zkPath = topicDirs.consumerOffsetDir
zkPath
} /**
* 获取偏移量信息
* @param children 分区数
* @param zkPath zookeeper中的topic信息的路径
* @param earlistLeaderOffsets broker中的实际最小偏移量
* @param latestLeaderOffsets broker中的实际最大偏移量
* @return
*/
private def getOffsets(children:Int,zkPath:String,earlistLeaderOffsets:Map[TopicAndPartition, KafkaCluster.LeaderOffset],latestLeaderOffsets: Map[TopicAndPartition, KafkaCluster.LeaderOffset]): Map[TopicAndPartition, Long] = {
var fromOffsets: Map[TopicAndPartition, Long] = Map()
for(i <- 0 until children){
//获取zookeeper记录的分区偏移量
val zkOffset = zkClient.readData[String](s"${zkPath}/${i}").toLong
val tp = TopicAndPartition(topic,i)
//获取broker中实际的最小和最大偏移量
val earlistOffset: Long = earlistLeaderOffsets(tp).offset
val latestOffset: Long = latestLeaderOffsets(tp).offset
//将实际的偏移量和zookeeper记录的偏移量进行对比,如果zookeeper中记录的偏移量在实际的偏移量范围内则使用zookeeper中的偏移量,
//反之,使用实际的broker中的最小偏移量
if(zkOffset>=earlistOffset && zkOffset<=latestOffset) {
fromOffsets += (tp -> zkOffset)
}else{
fromOffsets += (tp -> earlistOffset)
}
}
fromOffsets
} /**
* 创建DStream
* @param ssc
* @return
*/
def createDirectStream(ssc:StreamingContext):InputDStream[(String, String)]={
//----------------------获取broker中实际偏移量---------------------------------------------
val partitionsE: Either[Err, Set[TopicAndPartition]] = kc.getPartitions(topics)
if(partitionsE.isLeft)
throw new SparkException("get kafka partitions failed:")
val partitions = partitionsE.right.get
val earlistLeaderOffsetsE: Either[Err, Map[TopicAndPartition, KafkaCluster.LeaderOffset]] = kc.getEarliestLeaderOffsets(partitions)
if(earlistLeaderOffsetsE.isLeft)
throw new SparkException("get kafka earlistLeaderOffsets failed:")
val earlistLeaderOffsets: Map[TopicAndPartition, KafkaCluster.LeaderOffset] = earlistLeaderOffsetsE.right.get
val latestLeaderOffsetsE: Either[Err, Map[TopicAndPartition, KafkaCluster.LeaderOffset]] = kc.getLatestLeaderOffsets(partitions)
if(latestLeaderOffsetsE.isLeft)
throw new SparkException("get kafka latestLeaderOffsets failed:")
val latestLeaderOffsets: Map[TopicAndPartition, KafkaCluster.LeaderOffset] = latestLeaderOffsetsE.right.get
//----------------------创建kafkaStream----------------------------------------------------
var kafkaStream:InputDStream[(String, String)]=null
val zkPath: String = getZkPath()
val children = zkClient.countChildren(zkPath)
//根据zookeeper中是否有偏移量数据判断有没有消费过kafka中的数据
if(children > 0){
val fromOffsets:Map[TopicAndPartition, Long] = getOffsets(children,zkPath,earlistLeaderOffsets,latestLeaderOffsets)
val messageHandler = (mmd: MessageAndMetadata[String, String]) => (mmd.topic, mmd.message())
//如果消费过,根据偏移量创建Stream
kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder, (String, String)](
ssc, kafkaPrams, fromOffsets, messageHandler)
}else{
//如果没有消费过,根据kafkaPrams配置信息从最早的数据开始创建Stream
kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaPrams, topics)
}
kafkaStream
} /**
* 更新zookeeper中的偏移量
* @param offsetRanges
*/
def updateZkOffsets(offsetRanges:Array[OffsetRange])={
val zkPath: String = getZkPath()
for( o <- offsetRanges){
val newZkPath = s"${zkPath}/${o.partition}"
//将该 partition 的 offset 保存到 zookeeper
ZkUtils.updatePersistentPath(zkClient, newZkPath, o.fromOffset.toString)
}
}
}

  

驱动类:

package CC

import org.apache.spark.SparkConf
import org.apache.spark.sparkStreaming.kafka.KafkaHelper
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka.{HasOffsetRanges, OffsetRange}
import org.apache.spark.streaming.{Seconds, StreamingContext} /**
* Created by angel;
*/
object TestKafkaHelper {
def main(args: Array[String]): Unit = { val Array(timeInterval,brokerList,zkQuorum,topic,group) = Array(
"2"
, "hadoop01:9092,hadoop02:9092,hadoop03:9092"
, "hadoop01:2181,hadoop02:2181,hadoop03:2181"
, "CustomerContacts"
, "CustomerContacts"
) val conf = new SparkConf().setAppName("KafkaDirectStream").setMaster("local[2]")
val ssc = new StreamingContext(conf,Seconds(timeInterval.toInt)) //kafka配置参数
val kafkaParams = Map(
"metadata.broker.list" -> brokerList,
"group.id" -> group,
"auto.offset.reset" -> kafka.api.OffsetRequest.SmallestTimeString
) val kafkaHelper = new KafkaHelper(kafkaParams,zkQuorum,topic,group) val kafkaStream: InputDStream[(String, String)] = kafkaHelper.createDirectStream(ssc) var offsetRanges = Array[OffsetRange]() kafkaStream.transform( rdd =>{
offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
rdd
}).map( msg => msg._2)
.foreachRDD( rdd => {
rdd.foreachPartition( partition =>{
partition.foreach( record =>{
//处理数据的方法
println(record)
})
})
kafkaHelper.updateZkOffsets(offsetRanges)
}) ssc.start()
ssc.awaitTermination()
ssc.stop()
} }

  

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. VS2017编译LevelDB

    环境: 操作系统:Win7 x64 编译器:VS2017 需要Boost库支持,需要先将Boost库编译成为64位版本. 一.项目文件导入 1. 下载leveldb-windows,https://c ...

  2. PHP导入导出csv文件 Summer-CSV

    2017年11月9日09:25:56 根据项目实践总结的一个类文件, mac/win下没乱码 简体中文 默认从gb2312转到utf-8 https://gitee.com/myDcool/PHP-C ...

  3. Go语言环境安装&搭建(Win)

    Go语言简介 什么是Go语言 Go语言是谷歌2009发布的第二款开源编程语言. Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全.支持 ...

  4. 高性能MySQL(第3版) 中文PDF带目录清晰版

    下载地址: <高性能MySQL(第3版)>编辑推荐:"只要你不敢以MySQL专家自诩,又岂敢错过这本神书?""一言以蔽之,写得好,编排得好,需要参考时容易到爆 ...

  5. Mybatis调用PostgreSQL存储过程实现数组入参传递

    注:本文来源于 < Mybatis调用PostgreSQL存储过程实现数组入参传递  > 前言 项目中用到了Mybatis调用PostgreSQL存储过程(自定义函数)相关操作,由于Pos ...

  6. ubuntu sublime text 3 安装

    #安装GPG wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add - #确保apt被设置为 ...

  7. Python GUI界面编程

    常用GUI框架 wxPython 安装wxPython pip install -U wxPython C:\Users> pip install -U wxPython Collecting ...

  8. 制作linux下的.run安装包

    前言 之前往linux上安装一个软件,都是以压缩包或者压缩包+shell的方法,这每次安装,都是先scp到某个目录, 解压,安装......稍微厉害的,会写个shell脚本.但是还是达不到真正的快速方 ...

  9. tomcat启动报错 关键字:java.lang.NoClassDefFoundError和 java.lang.ClassNotFoundExceeption

    启动tomcat时报错情况如下图所示:实际上就是依赖的bean出错,百度上很多方法都说是tomcat没有部署正确,项目-->右键----->proterties----->Targe ...

  10. Error: Java VM internal error:Error Loading javai.dll

    因为前几天的JMS测试,第一次写了loadrunner的脚本,感觉路一下子宽了. 知道loadrunner可以使用java写脚本,今天就试了一下,遇到了两个第一次写Java Vuser脚本普遍都会遇到 ...