SparkStreaming消费Kafka,手动维护Offset到Mysql
说明
当前处理只实现手动维护offset到mysql,只能保证数据不丢失,可能会重复
要想实现精准一次性,还需要将数据提交和offset提交维护在一个事务中
官网说明
Your own data store
For data stores that support transactions, saving offsets in the same transaction as the results can keep the two in sync, even in failure situations. If you’re careful about detecting repeated or skipped offset ranges, rolling back the transaction prevents duplicated or lost messages from affecting results. This gives the equivalent of exactly-once semantics. It is also possible to use this tactic even for outputs that result from aggregations, which are typically hard to make idempotent.
您自己的数据存储
对于支持事务的数据存储,即使在失败情况下,将偏移与结果保存在同一事务中也可以使两者保持同步。 如果您在检测重复或跳过的偏移量范围时很谨慎,则回滚事务可防止重复或丢失的消息影响结果。 这相当于一次语义。 即使是由于聚合而产生的输出(通常很难使等幂),也可以使用此策略。

整体逻辑

offset建表语句
CREATE TABLE `offset_manager` (
`groupid` varchar(50) DEFAULT NULL,
`topic` varchar(50) DEFAULT NULL,
`partition` int(11) DEFAULT NULL,
`untiloffset` mediumtext,
UNIQUE KEY `offset_unique` (`groupid`,`topic`,`partition`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

代码实现
在线教育:知识点实时统计
import java.sql.{Connection, ResultSet}
import com.atguigu.qzpoint.util.{DataSourceUtil, QueryCallback, SqlProxy}
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.TopicPartition
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, HasOffsetRanges, KafkaUtils, LocationStrategies, OffsetRange}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.SparkConf
import scala.collection.mutable
/**
* @description: 知识点掌握实时统计
* @author: HaoWu
* @create: 2020年10月13日
*/
object QzPointStreaming_V2 {
val groupid = "test1"
def main(args: Array[String]): Unit = {
/**
* 初始化ssc
*/
val conf: SparkConf = new SparkConf()
.setAppName("test1")
.setMaster("local[*]")
.set("spark.streaming.kafka.maxRatePerPartition", "100")
.set("spark.streaming.backpressure.enabled", "true")
val ssc = new StreamingContext(conf, Seconds(3))
/**
* 读取mysql历史的offset
*/
val sqlProxy = new SqlProxy()
val client: Connection = DataSourceUtil.getConnection
val offsetMap = new mutable.HashMap[TopicPartition, Long]
try {
sqlProxy.executeQuery(client, "select * from `offset_manager` where groupid=?", Array(groupid), new QueryCallback {
override def process(rs: ResultSet): Unit = {
while (rs.next()) {
val model = new TopicPartition(rs.getString(2), rs.getInt(3))
val offset = rs.getLong(4)
offsetMap.put(model, offset)
}
rs.close()
}
})
} catch {
case e: Exception => e.printStackTrace()
} finally {
sqlProxy.shutdown(client)
}
/**
* 消费kafka主题,获取数据流
*/
val topics = Array("qz_log")
val kafkaMap: Map[String, Object] = Map[String, Object](
"bootstrap.servers" -> "hadoop102:9092,hadoop103:9092,hadoop104:9092",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> groupid,
"auto.offset.reset" -> "earliest",
//手动维护offset,要设置为false
"enable.auto.commit" -> (false: Boolean)
)
val inStream: InputDStream[ConsumerRecord[String, String]] = if (offsetMap.isEmpty) {
//第一次启动程序消费
KafkaUtils.createDirectStream(
ssc, LocationStrategies.PreferConsistent, ConsumerStrategies.Subscribe[String, String](topics, kafkaMap))
} else {
//程序挂了,恢复程序
KafkaUtils.createDirectStream(
ssc, LocationStrategies.PreferConsistent, ConsumerStrategies.Subscribe[String, String](topics, kafkaMap, offsetMap))
}
//*************************************************处理逻辑 开始**********************************************//
/**
* 逻辑处理的套路:统计当前批 + DB中历史的数据 => 更新DB中的表数据
*/
inStream
.filter(
record => record.value().split("\t") == 6
)
//*************************************************处理逻辑 结束**********************************************//
/**
* 逻辑处理完后,更新 mysql中维护的offset
*/
inStream.foreachRDD(rdd => {
val sqlProxy = new SqlProxy()
val client = DataSourceUtil.getConnection
try {
val offsetRanges: Array[OffsetRange] = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
for (or <- offsetRanges) {
sqlProxy.executeUpdate(client, "replace into `offset_manager` (groupid,topic,`partition`,untilOffset) values(?,?,?,?)",
Array(groupid, or.topic, or.partition.toString, or.untilOffset))
}
/*for (i <- 0 until 100000) {
val model = new LearnModel(1, 1, 1, 1, 1, 1, "", 2, 1l, 1l, 1, 1)
map.put(UUID.randomUUID().toString, model)
}*/
} catch {
case e: Exception => e.printStackTrace()
} finally {
sqlProxy.shutdown(client)
}
})
//启动
ssc.start()
//阻塞
ssc.awaitTermination()
}
}
SparkStreaming消费Kafka,手动维护Offset到Mysql的更多相关文章
- spark streaming读取kakfka数据手动维护offset
在spark streaming读取kafka的数据中,spark streaming提供了两个接口读取kafka中的数据,分别是KafkaUtils.createDstream,KafkaUtils ...
- Spark Streaming消费Kafka Direct保存offset到Redis,实现数据零丢失和exactly once
一.概述 上次写这篇文章文章的时候,Spark还是1.x,kafka还是0.8x版本,转眼间spark到了2.x,kafka也到了2.x,存储offset的方式也发生了改变,笔者根据上篇文章和网上文章 ...
- SparkStreaming消费kafka中数据的方式
有两种:Direct直连方式.Receiver方式 1.Receiver方式: 使用kafka高层次的consumer API来实现,receiver从kafka中获取的数据都保存在spark exc ...
- kafka手动设置offset
项目中经常有需求不是消费kafka队列全部的数据,取区间数据 查询kafka最大的offset: ./kafka-run-class.sh kafka.tools.GetOffsetShell --b ...
- Spring-Kafka —— 实现批量消费和手动提交offset
spring-kafka的官方文档介绍,可以知道自1.1版本之后, @KafkaListener开始支持批量消费,只需要设置batchListener参数为true 把application.yml中 ...
- sparkstreaming消费kafka后bulk到es
不使用es-hadoop的saveToES,与scala版本冲突问题太多.不使用bulkprocessor,异步提交,es容易oom,速度反而不快.使用BulkRequestBuilder同步提交. ...
- 使用spark-streaming实时读取Kafka数据统计结果存入MySQL
在这篇文章里,我们模拟了一个场景,实时分析订单数据,统计实时收益. 场景模拟 我试图覆盖工程上最为常用的一个场景: 1)首先,向Kafka里实时的写入订单数据,JSON格式,包含订单ID-订单类型-订 ...
- 17-Flink消费Kafka写入Mysql
戳更多文章: 1-Flink入门 2-本地环境搭建&构建第一个Flink应用 3-DataSet API 4-DataSteam API 5-集群部署 6-分布式缓存 7-重启策略 8-Fli ...
- SparkStreaming与Kafka,SparkStreaming接收Kafka数据的两种方式
SparkStreaming接收Kafka数据的两种方式 SparkStreaming接收数据原理 一.SparkStreaming + Kafka Receiver模式 二.SparkStreami ...
随机推荐
- 查看VCS版本的指令
以本人电脑的安装目录为例:在任意path下打开terminal执行命令行/usr/local/vcs2018/bin/vlogan -ID -full64 /usr/local/vcs2018为VCS ...
- hdu 2200 Eddy's AC难题(简单数学。。)
题意: N个人,每个人AC的题数都不一样. Eddy想从中选出一部分人(或者全部)分成两组.必须满足第一组中的最小AC数大于第二组中的最大AC数. 问共有多少种不同的选择方案. 思路: 简单数学.. ...
- Swarm+Docker+Portainer(集群,图形化)
参考文章 https://blog.csdn.net/u011781521/article/details/80469804 https://blog.csdn.net/u011781521/arti ...
- 使用gitlab runner 进行CI(四):使用Gitlab Page托管项目文档
目录 1.什么是Gitlab Pages 2.开启Gitlab Pages 3.基本过程 4.托管markdown文档 4.1 安装sphinx等依赖 4.2 配置项目的sphinx配置 4.3 编写 ...
- TDSQL | 在整个技术解决方案中HTAP对应的混合交易以及分析系统应该如何实现?
从主交易到传输,到插件式解决方案,每个厂商对HTAP的理解和实验方式都有自己的独到解法,在未来整个数据解决方案当中都会往HTAP中去牵引.那么在整个技术解决方案中HTAP对应的混合交易以及分析系统应该 ...
- Java反射判断对象实例所有属性是否为空
https://www.jb51.net/article/201647.htm public static Boolean ObjectAllFieldsEmpty(Object obj) throw ...
- Unity——技能系统(三)
Unity技能系统(三) Unity技能系统(一) Unity技能系统(二) Demo展示 六.Buff系统 buff分为增益和减益buff,应该区分开来: /// <summary> / ...
- GO的安装以及GoLand破解
GO的安装以及GoLand破解 GO的安装 GO语言中文网:GO语言中文网 go,GoLand,破解文件:JetBrains GoLand 2019.2.3 x64 提取码:ABCD(汉化文件也在其中 ...
- Salesforce Consumer Goods Cloud 浅谈篇四之店内拜访的创建和执行
本篇参考: https://v.qq.com/x/page/f0772toebhd.html https://v.qq.com/x/page/e0772tsmtek.html https://v.qq ...
- [loj3524]钥匙
由于到达关系具有传递性,可以考虑不断将若干个可以相互到达的点缩点,并且当两个点只能单向到达时,能到达另一个点的点一定不是最小值 由此,我们来考虑dfs,即不断从一个节点开始,遍历其可以到达的点,当发现 ...