spark streaming整合kafka中聚合类运算如何和kafka保持exactly once一致性语义(redis方式,利用pipeline)
2022/4/6 2:19:25
本文主要是介绍spark streaming整合kafka中聚合类运算如何和kafka保持exactly once一致性语义(redis方式,利用pipeline),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
/** * 从Kafka读取数据,实现ExactlyOnce,偏移量保存到Redis中 * 1.将聚合好的数据,收集到Driver端, * 2.然后将计算好的数据和偏移量在一个pipeline中同时保存到Redis中 * 3.成功了提交事物 * 4.失败了废弃原来的数据并让这个任务重启 */ object ExactlyOnceWordCountOffsetStoreInRedis { def main(args: Array[String]): Unit = { //true a1 g1 ta,tb val Array(isLocal, appName, groupId, allTopics) = args val conf = new SparkConf() .setAppName(appName) if (isLocal.toBoolean) { conf.setMaster("local[*]") } //创建StreamingContext,并指定批次生成的时间 val ssc = new StreamingContext(conf, Milliseconds(5000)) //设置日志级别 ssc.sparkContext.setLogLevel("WARN") //SparkStreaming 跟kafka进行整合 //1.导入跟Kafka整合的依赖 //2.跟kafka整合,创建直连的DStream【使用底层的消费API,效率更高】 val topics = allTopics.split(",") //SparkSteaming跟kafka整合的参数 //kafka的消费者默认的参数就是每5秒钟自动提交偏移量到Kafka特殊的topic中: __consumer_offsets val kafkaParams = Map[String, Object]( "bootstrap.servers" -> "node-1.51doit.cn:9092,node-2.51doit.cn:9092,node-3.51doit.cn:9092", "key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer", "value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer", "group.id" -> groupId, "auto.offset.reset" -> "earliest" //如果没有记录偏移量,第一次从最开始读,有偏移量,接着偏移量读 , "enable.auto.commit" -> (false: java.lang.Boolean) //消费者不自动提交偏移量 ) //在创建KafkaDStream之前要先读取Redis数据库,查询历史偏移量,没有就从头读,有就接着读 //offsets: collection.Map[TopicPartition, Long] val offsets: Map[TopicPartition, Long] = OffsetUtils.queryHistoryOffsetFromRedis(appName, groupId) //跟Kafka进行整合,需要引入跟Kafka整合的依赖 //createDirectStream更加高效,使用的是Kafka底层的消费API,消费者直接连接到Kafka的Leader分区进行消费 //直连方式,RDD的分区数量和Kafka的分区数量是一一对应的【数目一样】 val kafkaDStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String]( ssc, LocationStrategies.PreferConsistent, //调度task到Kafka所在的节点 ConsumerStrategies.Subscribe[String, String](topics, kafkaParams, offsets) //指定订阅Topic的规则 ) kafkaDStream.foreachRDD(rdd => { //判断当前批次的RDD是否有数据 if (!rdd.isEmpty()) { //获取RDD所有分区的偏移量 val offsetRanges: Array[OffsetRange] = rdd.asInstanceOf[HasOffsetRanges].offsetRanges //实现WordCount业务逻辑 val words: RDD[String] = rdd.flatMap(_.value().split(" ")) val wordsAndOne: RDD[(String, Int)] = words.map((_, 1)) val reduced: RDD[(String, Int)] = wordsAndOne.reduceByKey(_ + _) //将计算好的结果收集到Driver端再写入到Redis中【保证数据和偏移量写入在一个事物中】 //触发Action,将数据收集到Driver段 val res: Array[(String, Int)] = reduced.collect() var jedis: Jedis = null var pipeline: Pipeline = null //创建一个Redis的连接【在Driver端创建】 try { jedis = JedisConnectionPool.getConnection() //使用pipeline pipeline = jedis.pipelined() pipeline.select(1) //开启多个操作在一起执行 pipeline.multi() //写入计算好的结果 for (tp <- res) { pipeline.hincrBy("WORD_COUNT", tp._1, tp._2) } //写入偏移量 for (offsetRange <- offsetRanges) { val topic = offsetRange.topic val partition = offsetRange.partition val untilOffset = offsetRange.untilOffset //将原来的偏移量覆盖 pipeline.hset(appName +"_" + groupId, topic + "_" + partition, untilOffset.toString) } //类似提交事物 pipeline.exec() pipeline.sync() } catch { case e: Exception => { pipeline.discard() e.printStackTrace() ssc.stop() } } finally { pipeline.close() jedis.close() } } }) ssc.start() ssc.awaitTermination() } }
这篇关于spark streaming整合kafka中聚合类运算如何和kafka保持exactly once一致性语义(redis方式,利用pipeline)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-02阿里云Redis项目实战入门教程
- 2025-01-02阿里云Redis资料入门详解
- 2024-12-30阿里云Redis教程:新手入门指南
- 2024-12-27阿里云Redis学习入门指南
- 2024-12-27阿里云Redis入门详解:轻松搭建与管理
- 2024-12-27阿里云Redis学习:新手入门指南
- 2024-12-24Redis资料:新手入门快速指南
- 2024-12-24Redis资料:新手入门教程与实践指南
- 2024-12-24Redis资料:新手入门教程与实践指南
- 2024-12-07Redis高并发入门详解