streaming消费kafka手动维护offset到redis

1.redis工具类

复制代码
```scala
package com.qupojie.kafka_offset

import org.apache.kafka.common.TopicPartition
import org.apache.spark.streaming.kafka010.OffsetRange
import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig}
import java.util
import scala.collection.JavaConverters.mapAsScalaMapConverter
import scala.collection.mutable

object RedisUtils {

  private val config = new JedisPoolConfig
  private val redisHost = "hadoop110"
  private val redisPort = 6379
  config.setMaxTotal(30)
  config.setMaxIdle(10)
  private val pool = new JedisPool(config, redisHost, redisPort, 10000)
  private val topicPrefix = "kafka:topic"
  private val offsetMap: mutable.Map[TopicPartition, Long] = mutable.Map()
  private def getKey(topic: String, groupId: String, prefix: String = topicPrefix): String = s"$prefix:$topic:$groupId"
  private def getRedisConnection: Jedis = pool.getResource

  //获取redis offset
  def getOffsetsFromRedis(topics: Array[String], groupId: String): mutable.Map[TopicPartition, Long] = {
    val jedis: Jedis = getRedisConnection
    topics.foreach(topic => {
      jedis.select(1)
      val resultoffsetMap: util.Map[String, String] = jedis.hgetAll(getKey(topic, groupId))
      //关闭流
      jedis.close()
      //判断
      if (resultoffsetMap.size() == 0) {
        //如果没用读到redis数据就给初始化offset
        offsetMap.put(new TopicPartition(topic, 0), 0L)
        offsetMap.put(new TopicPartition(topic, 1), 0L)
        offsetMap.put(new TopicPartition(topic, 2), 0L)
        offsetMap
      } else {
        resultoffsetMap.asScala
          .foreach(offset => {
            //如果读到redis数据就封装map返回
            offsetMap.put(new TopicPartition(topic, offset._1.toInt), offset._2.toLong)
          })

      }
    })
    offsetMap
  }

  def saveOffsetsToRedis(ranges: Array[OffsetRange], groupId: String): Unit = {
    val jedis: Jedis = getRedisConnection
    jedis.select(1)
    val offsetList: Map[String, Array[(String, (Int, Long))]] = ranges
      .map(range => (range.topic, range.partition -> range.untilOffset))
      .groupBy(_._1)
    offsetList.map {
      case (topic, buffer) => (topic, buffer.map(_._2))
    }.foreach {
      case (topic, partitionAndOffset) =>
        val offsets: Array[(String, String)] = partitionAndOffset.map(elem => (elem._1.toString, elem._2.toString))
        import scala.collection.JavaConverters._
        jedis.hmset(getKey(topic, groupId), offsets.toMap.asJava)
    }
    jedis.close()
  }
}
```scala

2.spark streaming实现类

复制代码
```scala
package com.qupojie.kafka_offset

import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.kafka.common.TopicPartition
import org.apache.spark.{SparkConf, TaskContext}
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Assign
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.{HasOffsetRanges, KafkaUtils, LocationStrategies, OffsetRange}
import org.apache.spark.streaming.{Durations, Seconds, StreamingContext}

import scala.collection.mutable

object SparkConsumerKafka01 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("SparkConsumerKafka01").setMaster("local[*]")
    val sc: StreamingContext = new StreamingContext(conf = conf, batchDuration = Durations.seconds(5))
    val kafkaParams: mutable.HashMap[String, String] = new mutable.HashMap[String, String]()
    kafkaParams.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop110:9092,hadoop112:9092,hadoop112:9092");
    kafkaParams.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
    kafkaParams.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
    kafkaParams.put("spark.streaming.kafka.maxRatePerPartition", "10");
    kafkaParams.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
    var groupId = "SparkConsumerKafka01"
    kafkaParams.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
    kafkaParams.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); // 最新的位置
    val dbIndex = 1
    val topics = Array("spark_test02")
    val fromOffsets: mutable.Map[TopicPartition, Long] = RedisUtils.getOffsetsFromRedis(topics, "SparkConsumerKafka01")
    val stream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
      sc,
      PreferConsistent,
      Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets)
    )
    stream.foreachRDD(rdd => {
      println("--------当前时间"+System.nanoTime()+"--------")
      val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
      rdd.foreachPartition(iter => {
        val o: OffsetRange = offsetRanges(TaskContext.get.partitionId)
        println(s"${o.topic} 分区: ${o.partition} 起始偏移量:${o.fromOffset} 结束偏移量: ${o.untilOffset}")
        RedisUtils.saveOffsetsToRedis(offsetRanges, groupId)
      })
    })
    sc.start()
    sc.awaitTermination()
  }
}

```scala

3.运行图

相关推荐
Cxzzzzzzzzzz43 分钟前
RabbitMQ 在实际开发中的应用场景与实现方案
分布式·rabbitmq
在未来等你1 小时前
Kafka面试精讲 Day 16:生产者性能优化策略
大数据·分布式·面试·kafka·消息队列
王大帅の王同学1 小时前
Thinkphp6接入讯飞星火大模型Spark Lite完全免费的API
大数据·分布式·spark
一氧化二氢.h3 小时前
通俗解释redis高级:redis持久化(RDB持久化、AOF持久化)、redis主从、redis哨兵、redis分片集群
redis·分布式·缓存
爱睡觉的圈圈7 小时前
分布式IP代理集群架构与智能调度系统
分布式·tcp/ip·架构
APItesterCris9 小时前
构建分布式京东商品数据采集系统:基于 API 的微服务实现方案
分布式·微服务·架构
ouou061710 小时前
企业级NoSql数据库Redis集群
数据库·redis·nosql
free10 小时前
基于librdkafa C++客户端生产者发送数据失败问题处理#2
c++·kafka
不吃饭的猪10 小时前
kafka启动小脚本
分布式·kafka
lwprain10 小时前
龙蜥8.10中spark各种集群及单机模式的搭建spark3.5.6(基于hadoop3.3.6集群)
大数据·ajax·spark