kafka版本 1.0.0

spark版本 spark-streaming-kafka-0-10_2.11
/**
* @created by imp ON 2019/12/21
*/
class KafkaManagerByRedis(zkHost:String,kafkaParams: Map[String, Object]) extends Logging {

private val (zkClient,zkConnection) = ZkUtils.createZkClientAndConnection(zkHost , 10000 , 10000)
private val zkUtils = new ZkUtils(zkClient,zkConnection , false)
private val jedis = JedisUtil.getInstance().getJedis

/**
* def createDirectStream:InputDStream
**/

def createDirectStream[K: ClassTag, V: ClassTag](ssc: StreamingContext, topics: Seq[String]): InputDStream[ConsumerRecord[K, V]] = {
//1:readOffset
val groupId = kafkaParams("group.id").toString
val topic = topics(0)
val topicPartition: Map[TopicPartition, Long] = readOffset(topic, groupId)
KafkaUtils.createDirectStream[K, V](
ssc,
PreferConsistent,
Subscribe[K, V](topics, kafkaParams, topicPartition)
)
}

/**
* 读取偏移量
*
* @param topics
* @param groupId 消费组
* @return Map[car-1 , car-2 , Long]
**/

private def readOffset(topic: String, groupId: String): Map[TopicPartition, Long] = {
val topicPartitionMap = collection.mutable.HashMap.empty[TopicPartition, Long]
//去zk上拿topic和分区信息
val topicAndPartitionMaps: mutable.Map[String, Seq[Int]] = zkUtils.getPartitionsForTopics(Seq(topic))
val groupId = kafkaParams("group.id").toString
val redisKey = topic + "|" + groupId
topicAndPartitionMaps.foreach(topicPartitions =>{
val zkGroupTopicsDirs: ZKGroupTopicDirs = new ZKGroupTopicDirs(groupId , topicPartitions._1)
topicPartitions._2.foreach(partition => {
//迭代分区
val map: util.Map[String, String] = jedis.hgetAll(redisKey)
val offsetMap: mutable.Map[String, String] = mapAsScalaMap(map)
if (offsetMap != null && offsetMap.size != 0) {
logger.error("groupId:"+groupId+"获取到redis的偏移量数据")
topicPartitionMap.put(new TopicPartition(topicPartitions._1, Integer.valueOf(partition)), offsetMap(partition.toString).toLong)
}
else {
logger.error("程序第一次启动,redis还未存储,获取kafka的偏移量")
val consumer = new KafkaConsumer[String, Object](kafkaParams)
val topicCollection = List(new TopicPartition(topicPartitions._1 , partition))
consumer.assign(topicCollection)
val avaliableOffset: Long = consumer.beginningOffsets(topicCollection).values().head
consumer.close()
topicPartitionMap.put(new TopicPartition(topicPartitions._1 , Integer.valueOf(partition)) , avaliableOffset)
}
})
}
)

//currentoffset 、 earliestoffset leatestOffset
//cur < ear || cur > leaty ==> 矫正--> ear
//TODO 矫正
val earliestOffsets = getEarliestOffsets(kafkaParams, topic)
val topics = List(topic)
val latestOffsets = getLatestOffsets(kafkaParams, topics)
for ((k, v) <- topicPartitionMap) {
val current = v
val earliest = earliestOffsets.get(k).get
val latest = latestOffsets.get(k).get
if (current < earliest || current > latest) {
topicPartitionMap.put(k, earliest)
}
}
topicPartitionMap.toMap
}

/**
* 获取最早的偏移量
*
* @param kafkaParams
* @param topics
* @return
*/
private def getEarliestOffsets(kafkaParams: Map[String, Object], topic: String) = {
val newKafkaParams = mutable.Map[String, Object]()
newKafkaParams ++= kafkaParams
newKafkaParams.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest")
//kafka api
val consumer = new KafkaConsumer(kafkaParams)
//订阅
val topics = Seq[String](topic)
consumer.subscribe(topics)
val noOffsetForPartitionExceptionSet: mutable.Set[Nothing] = mutable.Set()
try {
consumer.poll(0)
} catch {
case e: NoOffsetForPartitionException =>
// noOffsetForPartitionExceptionSet.add(e.partition())
//邮件报警
}
//获取 分区信息
val topicp = consumer.assignment().toSet
//暂定消费
consumer.pause(topicp)
//从头开始
consumer.seekToBeginning(topicp)
val toMap = topicp.map(line => line -> consumer.position(line)).toMap
val earliestOffsetMap = toMap
consumer.unsubscribe()
consumer.close()
earliestOffsetMap
}

private def getLatestOffsets(kafkaParams: Map[String, Object], topic: Seq[String]) = {
val newKafkaParams = mutable.Map[String, Object]()
newKafkaParams ++= kafkaParams
newKafkaParams.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")

//kafka api
val consumer = new KafkaConsumer[String, Array[Byte]](newKafkaParams)
//订阅
consumer.subscribe(topic)
val noOffsetForPartitionExceptionSet = mutable.Set()
try {
consumer.poll(0)
} catch {
case e: NoOffsetForPartitionException =>
// noOffsetForPartitionExceptionSet.add(e.partition())
//邮件报警
}
//获取 分区信息
val topicp = consumer.assignment().toSet
//暂定消费
consumer.pause(topicp)
//从尾开始
consumer.seekToEnd(topicp)
val toMap: Map[TopicPartition, Long] = topicp.map(line => line -> consumer.position(line)).toMap
val earliestOffsetMap = toMap
consumer.unsubscribe()
consumer.close()
earliestOffsetMap
}

def persistOffset[K, V](rdd: RDD[ConsumerRecord[K, V]], storeOffset: Boolean = true, topic: String) = {
val groupId = kafkaParams("group.id").toString
val offsetRanges: Array[OffsetRange] = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
offsetRanges.foreach(offsetRange => {
val redisKey = topic + "|" + groupId
val data = if (storeOffset) offsetRange.untilOffset else offsetRange.fromOffset
jedis.hset(redisKey, offsetRange.partition.toString, data.toString)
println("topic:" + offsetRange.topic + "分区:" + offsetRange.partition + "开始消费" + offsetRange.fromOffset + "消费到" + offsetRange.untilOffset + "共计" + offsetRange.count())
})

}

}

object KafkaManagerByRedis {
def main(args: Array[String]): Unit = {
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "192.168.121.12:9092,192.168.121.12:9093",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "test1",
"auto.offset.reset" -> ("earliest "),
"enable.auto.commit" -> (false: java.lang.Boolean) //禁用自动提交Offset,否则可能没正常消费完就提交了,造成数据错误
)
val zkServer=""
val kafkama = new KafkaManagerByRedis(zkServer,kafkaParams)
kafkama.getEarliestOffsets(kafkaParams, "cheng_du_gps_topic")
.foreach(m => println(m._1.topic(), m._1.partition(), m._2))

kafkama.getLatestOffsets(kafkaParams, List("cheng_du_gps_topic"))
.foreach(m => println(m._1.topic(), m._1.partition(), m._2))
}
}

  1.  

kafka-spark偏移量提交至redis kafka1.0版本的更多相关文章

  1. Redis 3.0版本启动时出现警告的解决办法

    原文:http://m.blog.csdn.net/article/details?id=50864933 Redis 3.0.7版本启动时出现警告的解决办法 发表于2016/3/12 12:52:4 ...

  2. centos安装redis 5.0版本的集群

    我在本地VM-Centos里安装5.0.5时安装遇到了些问题,参考了Blog:https://www.cnblogs.com/shawhe/p/9548620.html 顺利安装完成. 安装redis ...

  3. Redis 3.0正式版发布,正式支持Redis集群

    Redis是一个开源.基于C语言.基于内存亦可持久化的高性能NoSQL数据库,同时,它还提供了多种语言的API.近日,Redis 3.0在经过6个RC版本后,其正式版终于发布了.Redis 3.0的最 ...

  4. 【转载】Redis 4.0 自动内存碎片整理(Active Defrag)源码分析

    click原文链接原文链接:https://blog.csdn.net/zouhuajianclever/article/details/90669409阅读本文前建议先阅读此篇博客: Redis源码 ...

  5. 阿里云发布 Redis 5.0 缓存服务:全新 Stream 数据类型带来不一样缓存体验

    4月24日,阿里云正式宣布推出全新 Redis 5.0 版本云数据库缓存服务,据悉该服务完全兼容 4.0 及早期版本,继承了其一贯的安全,稳定,高效等特点并带来了全新的 Stream 数据结构及多项优 ...

  6. Redis 源码简洁剖析 11 - 主 IO 线程及 Redis 6.0 多 IO 线程

    Redis 到底是不是单线程的程序? 多 IO 线程的初始化 IO 线程运行函数 IOThreadMain 如何推迟客户端「读」操作? 如何推迟客户端「写」操作? 如何把待「读」客户端分配给 IO 线 ...

  7. demo2 Kafka+Spark Streaming+Redis实时计算整合实践 foreachRDD输出到redis

    基于Spark通用计算平台,可以很好地扩展各种计算类型的应用,尤其是Spark提供了内建的计算库支持,像Spark Streaming.Spark SQL.MLlib.GraphX,这些内建库都提供了 ...

  8. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十三)kafka+spark streaming打包好的程序提交时提示虚拟内存不足(Container is running beyond virtual memory limits. Current usage: 119.5 MB of 1 GB physical memory used; 2.2 GB of 2.1 G)

    异常问题:Container is running beyond virtual memory limits. Current usage: 119.5 MB of 1 GB physical mem ...

  9. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(九)安装kafka_2.11-1.1.0

    如何搭建配置centos虚拟机请参考<Kafka:ZK+Kafka+Spark Streaming集群环境搭建(一)VMW安装四台CentOS,并实现本机与它们能交互,虚拟机内部实现可以上网.& ...

随机推荐

  1. AH/HNOI 2017 礼物

    题目链接 描述 两个序列 \(x, y\),可以将一个序列每个值同时加非负整数 \(c\),其中一个序列可以循环移位,要求最小化: \[\sum_{i = 1}^{n}(x_i - y_i) ^ 2 ...

  2. 题解-SHOI2005 树的双中心

    SHOI2005 树的双中心 给树 \(T=(V,E)(|V|=n)\),树高为 \(h\),\(w_u(u\in V)\).求 \(x\in V,y\in V:\left(\sum_{u\in V} ...

  3. SpringBoot+Redis相关配置文件

    springboot整合redis配置类 package com.yalong.config; import com.fasterxml.jackson.annotation.JsonAutoDete ...

  4. Java后端使用socketio,实现小程序答题pk功能

    在使用socket.io跟前端通信过程中,出现了一系列问题,现做下记录. 一.功能需求是,在小程序端,用户可相互邀请,进入房间后进行答题PK.实现方法是,用户点击邀请好友,建立连接,查询当前是否有房间 ...

  5. 【QT】多个槽函数绑定同一个信号的触发顺序

    目录 一.Qt 3.0(包含3.0) - Qt 4.5(包含4.5)版本之前 二.Qt 4.6(包含4.6)版本之后 一.Qt 3.0(包含3.0) - Qt 4.5(包含4.5)版本之前 「多个槽函 ...

  6. js下 Day18、综合案例

    一.分页 效果图: 功能思路分析: 分页就是将所有的数据按指定条数分成若干份: 假如有24条数据,每页只显示5条,则需要分成Math.ceil(24 / 5) = 5页; 每次只显示1页数据,所以需要 ...

  7. 算法(Java实现)—— 动态规划算法

    动态规划算法 应用场景-0-1背包问题 背包问题:有一个背包,容量为4磅,现有物品如下 物品 重量 价格 吉他(G) 1 1500 音响(S) 4 3000 电脑(L) 3 2000 要求: 达到目标 ...

  8. JMeter上传文件,并修改源码参数化Content-Disposition 的 filename

    一.JMeter上传文件 1.使用F12或抓包工具抓包对应接口 如下图为一个上传图片接口,抓包显示内容如下: 2.将抓包到的信息头内容填写到jmeter的HTTP信息头管理器 3.填写参数 由抓包的接 ...

  9. 手把手教你搭饥荒专用服务器(三)—MOD及其他高级设置

    友情链接: 手把手教你搭饥荒专用服务器(一)-服务器准备工作 手把手教你搭饥荒专用服务器(二)-环境配置及基本使用 手把手教你搭饥荒专用服务器(三)-MOD及其他高级设置 手把手教你搭饥荒专用服务器( ...

  10. ReentrantLock锁-CAS与阻塞

    ReentrantLock锁 ReentrantLock通过原子操作和阻塞实现锁原理,一般使用lock获取锁,unlock释放锁 lock的时候可能被其他线程获得所,那么此线程会阻塞自己,关键原理底层 ...