sparkStreaming消费kafka-0.8方式:direct方式(存储offset到zookeeper)
生产中,为了保证kafka的offset的安全性,并且防止丢失数据现象,会手动维护偏移量(offset)
版本:kafka:0.8
其中需要注意的点:
1:获取zookeeper记录的分区偏移量
2:获取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)的更多相关文章
- SparkStreaming获取kafka数据的两种方式:Receiver与Direct
简介: Spark-Streaming获取kafka数据的两种方式-Receiver与Direct的方式,可以简单理解成: Receiver方式是通过zookeeper来连接kafka队列, Dire ...
- SparkStreaming与Kafka,SparkStreaming接收Kafka数据的两种方式
SparkStreaming接收Kafka数据的两种方式 SparkStreaming接收数据原理 一.SparkStreaming + Kafka Receiver模式 二.SparkStreami ...
- sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)-- 2
参考上篇博文:https://www.cnblogs.com/niutao/p/10547718.html 同样的逻辑,不同的封装 package offsetInZookeeper /** * Cr ...
- sparkStreaming消费kafka-1.0.1方式:direct方式(存储offset到zookeeper)
版本声明: kafka:1.0.1 spark:2.1.0 注意:在使用过程中可能会出现servlet版本不兼容的问题,因此在导入maven的pom文件的时候,需要做适当的排除操作 <?xml ...
- SparkStreaming消费kafka中数据的方式
有两种:Direct直连方式.Receiver方式 1.Receiver方式: 使用kafka高层次的consumer API来实现,receiver从kafka中获取的数据都保存在spark exc ...
- Spark-Streaming获取kafka数据的两种方式:Receiver与Direct的方式
简单理解为:Receiver方式是通过zookeeper来连接kafka队列,Direct方式是直接连接到kafka的节点上获取数据 Receiver 使用Kafka的高层次Consumer API来 ...
- 解析SparkStreaming和Kafka集成的两种方式
spark streaming是基于微批处理的流式计算引擎,通常是利用spark core或者spark core与spark sql一起来处理数据.在企业实时处理架构中,通常将spark strea ...
- 工具篇-Spark-Streaming获取kafka数据的两种方式(转载)
转载自:https://blog.csdn.net/weixin_41615494/article/details/7952173 一.基于Receiver的方式 原理 Receiver从Kafka中 ...
- spark-streaming获取kafka数据的两种方式
简单理解为:Receiver方式是通过zookeeper来连接kafka队列,Direct方式是直接连接到kafka的节点上获取数据 一.Receiver方式: 使用kafka的高层次Consumer ...
随机推荐
- linux dynamic lib
// test1.h ; struct AA { int a,b: }; AA b(5,6); int ball(); // test1.cpp # include"test1.h" ...
- 构造函数中base与this的区别
base是对父类的引用,而this是对类本身的引用. namespace ConsoleApplication1 { public class BaseClass { private string n ...
- WinCE平台的程序编译到Win32平台下运行
最近做的项目中,有一个在WinCE平台上跑的程序,后来随着项目的发展,要求此程序在PC上也能跑.感谢VS 2005提供的多平台支持,只需要几分钟就可以解决这个问题,方法很简单,下面是我处理的过程. 1 ...
- push to origin/master was rejected错误解决方案
idea中,发布项目到OSChina的Git中,当时按照这样的流程添加Git,然后push,提示:push to origin/master war rejected". 解决方案如下: 1 ...
- hbase搭建
0. 软件版本下载 http://mirror.bit.edu.cn/apache/hbase/ 1. 集群环境 Master 172.16.11.97 Slave1 172.16.11.98 S ...
- Exception类的学习与继承总结
日期:2018.11.11 星期日 博客期:023 Exception类的学习与继承总结 说起来我们上课还是说过的!老师提到了报错问题出现主要分Exception和Error两类!第一次遇见这个问题是 ...
- shell中的ps命令详解
ps简介:Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的 ...
- hdu4966 最小树形图+虚根
/* 辛辛苦苦调试半天, 过了样例,竟然没有ac!! 网上对比了ac代码,感觉添加一个虚根就能ac 但是想不明白为什么 */ /* 第二天想了下,知道了为什么wa:因为从等级0连到其他课程等级i的不止 ...
- Linux系统下inode满了导致无法写文件的解决思路
解决思路1:删除无用的临时文件,释放inode 进入/tmp目录,执行find -exec命令 find /tmp -type f -exec rm {} \; 遍历寻找0字节的文件,并 ...
- 使用springboot actuator监控应用
微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的处理和传递,出现了异常如何快速定位是哪个环节出现了问题? ...