生产中,为了保证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. python操作三大主流数据库(8)python操作mongodb数据库②python使用pymongo操作mongodb的增删改查

    python操作mongodb数据库②python使用pymongo操作mongodb的增删改查 文档http://api.mongodb.com/python/current/api/index.h ...

  2. mysql alter 效率

    2017年9月15日 10:36:54 星期五 今天遇到一个效率问题记下来: 场景: mysql要更改一下表字段的注释, 因为sql语句问题, 导致更新了整张表.. 错误: ) UNSIGNED ' ...

  3. java结合testng,利用excel做数据源的数据驱动实例

    数据驱动部分,是自动化测试常用部分,也是参数化设计的重要环节,前面分享了,mysql.yaml做数据源,那么再来分享下excel做数据驱动 思路: 先用POI读取excel.解析读取数据,返回list ...

  4. JAVA实现网络文件下载

    HttpURLConnection conn = null; OutputStream outputStream = null; InputStream inputStream = null; try ...

  5. CSS 媒体查询@media

    1. 概述 1.1 定义 @media可以针对不同的屏幕尺寸(媒体类型)设置不同的样式,在响应式页面中,@media非常有用.重置浏览器大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面. 1. ...

  6. Tornado学习笔记(一) helloword/多进程/启动参数

    前言 当你觉得你过得很舒服的时候,你肯定没有在进步.所以我想学习新的东西,然后选择了Tornado.因为我觉得Tornado更匹配目前的我的综合素质. Tornado学习笔记系列主要参考<int ...

  7. 12)django-ORM(单表返回数据3种方式)

    单表查询还回数据有3种形式,返回形式不一样,模板调用方式不同 1)返回Queryset里面内容为对象:Business.objects.all() 这里内容显示是对象 2)返回Queryset里面内容 ...

  8. 如何将代码通过vs2017加载到GitHub

    (1)登陆GitHub并注册账户,在用户中新建repository  (2)建立后,会给出新建repository地址,将其复制  (3)用VS新建一个项目,勾选“新建Git存储库”或者打开一个已经创 ...

  9. MSChart的研究(转)

    介绍MSChart的常用属性和事件 MSChart的元素组成 最常用的属性包括 ChartAreas:增加多个绘图区域,每个绘图区域包含独立的图表组.数据源,用于多个图表类型在一个绘图区不兼容时. A ...

  10. Android&Java面试题大全—金九银十面试必备

    声明本文由作者:Man不经心授权转载,转载请联系原文作者原文链接:https://www.jianshu.com/p/375ad14096b3, 类加载过程 Java 中类加载分为 3 个步骤:加载. ...