sparkstreaming 读取mysql_sparkstreaming遇到的问题

本文探讨了SparkStreaming在读取MySQL数据时遇到的offset问题,包括numRecords must not be negative和Offsets out of range。解决方案涉及手动维护offset,如在offset存储到MySQL时进行异常处理和矫正。当遇到非法offset时,需要及时矫正,确保数据的准确消费。
摘要由CSDN通过智能技术生成

ad67b568822a27826c0d7df7a2945b77.png

这篇文章介绍sparkstreaming对接kafka时遇到的两个offset的问题,首选我们介绍下offset的存储。

sparkstreaming offset存储

sparkstreaming采用kafkaUtils的createDirectStream()处理kafka数据的方式,会直接从kafka的broker的分区中读取数据,跳过zookeeper,并且没有receiver,是spark的task直接对接kafka topic partition。

由于这种方式没有经过ZK,topic的offset没有保存,当job重启后只能从最新的offset开始消费数据,造成重启过程中的消息丢失。

如果spark自动提交,会在sparkstreaming刚运行时就立马提交offset,如果这个时候Spark streaming消费信息失败了,那么offset也就错误提交了。

所以要在sparkstreaming中实现exactly-once恰好一次,必须

1.手动提交偏移量

2.处理完业务数据后再提交offset

手动维护偏移量 需设置kafka参数enable.auto.commit改为false

手动维护提交offset有两种选择:

1.处理完业务数据后手动提交到Kafka

     2.处理完业务数据后手动提交到本地库 如MySql、HBase

也可以将offset提交到zookeeper,但是经过我们测试,发现zookeeper不适合存储大量数据,在大数据量的情况下很容易崩溃。

我们来看下如何将offset存储到mysql中:

// 处理完 业务逻辑后,手动提交offset偏移量到本地Mysql中
stream.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))
}
} catch {
case e: Exception =< e.printStackTrace()
} finally {
sqlProxy.shutdown(client)
}
})

HBase中也是类似的

inputDStream.foreachRDD((rdd, batchTime) =&gt; {
    
val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
offsetRanges.foreach(offset =&gt; println(offset.topic, offset.partition, offset.fromOffset, offset.untilOffset))
val newRDD = rdd.map(message =&gt; processMessage(message))
newRDD.count()
//save the offsets to HBase 批量处理把数据存储到Hbase当中
saveOffsets(topic, consumerGroupID, offsetRanges, hbaseTableName, batchTime)
})
ssc
}


/**
* 对数据进行处理
* @param message
* @return
*/
def processMessage(message: ConsumerRecord[String, String]): ConsumerRecord[String, String] = {
message
}


/*
Save Offsets into HBase
*/
def saveOffs
可以使用JDBC(Java数据库连接)来将MySQL数据源加载到Spark Streaming中。具体来说,可以通过以下步骤实现: 1. 在Spark Streaming中创建一个StreamingContext对象。 2. 创建一个JDBC连接,连接到MySQL数据库。 3. 使用Spark Streaming内置的DStream对象创建一个输入流,该输入流将使用提供的数据源从MySQL数据库中读取数据。 4. 对每个批次的数据执行必要的转换和操作。 5. 将数据写回MySQL数据库。 下面是一个示例代码: ```python from pyspark.streaming import StreamingContext from pyspark import SparkContext, SparkConf from pyspark.sql import SQLContext import datetime conf = SparkConf().setAppName("MySQLStream") sc = SparkContext(conf=conf) sqlContext = SQLContext(sc) ssc = StreamingContext(sc, batchDuration=1) # 创建JDBC连接 jdbcUrl = "jdbc:mysql://localhost:3306/mydatabase" jdbcUsername = "username" jdbcPassword = "password" # 使用DStream对象创建输入流 def createMySQLStream(): inputDF = sqlContext.read.format("jdbc").options(url=jdbcUrl, dbtable="mytable", user=jdbcUsername, password=jdbcPassword).load() stream = inputDF.writeStream.outputMode("append").format("console").start() return stream # 对每个批次的数据执行必要的转换和操作 def processStream(batchTime, rdd): if not rdd.isEmpty(): # 在这里处理批次的数据 processedData = rdd # 将数据写回MySQL数据库 processedData.write.jdbc(url=jdbcUrl, table="outputtable", mode="append", properties={"user": jdbcUsername, "password": jdbcPassword}) # 创建DStream对象,并开始流处理 stream = createMySQLStream() stream.foreachRDD(processStream) ssc.start() ssc.awaitTermination() ``` 注意:这只是一个简单的示例,实际生产环境中需要进行更多的错误处理和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值