kafka直连方式api与Redis

本文介绍如何使用Kafka直连API进行数据流处理,包括配置依赖、创建Spark Streaming DStream,以及如何将数据保存到Redis中。同时,详细讲解了Redis连接池的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

kafka作为生产者,把生产的数据保存到Redis中,读取的是JSON文件,需要导入阿里的包

一、pom文件进行设置

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>com.typesafe</groupId>
    <artifactId>config</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>org.scalikejdbc</groupId>
    <artifactId>scalikejdbc_2.11</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>org.scalikejdbc</groupId>
    <artifactId>scalikejdbc-core_2.11</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>org.scalikejdbc</groupId>
    <artifactId>scalikejdbc-config_2.11</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.36</version>
</dependency>

二、kafka直连API

  import com.alibaba.fastjson.JSON
  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.SparkConf
  import org.apache.spark.streaming.dstream.InputDStream
  import org.apache.spark.streaming.kafka.{HasOffsetRanges, KafkaUtils, OffsetRange}
  import org.apache.spark.streaming.{Duration, StreamingContext}

  /**
    *   Direct直连方式
    */
  object KafkaDirectLink {
    def main(args: Array[String]): Unit = {
      val conf = new SparkConf().setAppName("Direct").setMaster("local[2]")
      val ssc = new StreamingContext(conf,Duration(5000))
      //指定组名
      val groupId = "gp01"
      //指定消费的topic名字
      val topic = "m"
      //指定kafka的Broker地址(SparkStreaming的Task直接连接到Kafka分区上,用的是底层API消费)
      val brokerList ="192.168.146.131:9092,192.168.146.132:9092,192.168.146.133:9092"
      //接下来我们要自己维护offset了,将offset保存到ZK中
      val zkQuorum = "192.168.146.131:2181,192.168.146.131:2181,192.168.146.131:2181"
      //创建stream时使用的topic名字集合,SparkStreaming可以同时消费多个topic
      val topics:Set[String] = Set(topic)
      //创建一个ZkGroupTopicDirs对象,其实是指定往Zk中写入数据的目录
      // 用于保存偏移量
      val TopicDirs = new ZKGroupTopicDirs(groupId,topic)
      //获取zookeeper中的路径“/gp01/offset/tt/”
      val zkTopicPath = s"${TopicDirs.consumerOffsetDir}"
      //准备kafka参数
      val kafkas = Map(
        "metadata.broker.list"->brokerList,
      "group.id"->groupId,
      //从头开始读取数据
      "auto.offset.reset"->kafka
        .api.OffsetRequest.SmallestTimeString
      )
      // zookeeper 的host和ip,创建一个client,用于更新偏移量
      // 是zookeeper客户端,可以从zk中读取偏移量数据,并更新偏移量
      val zkClient = new ZkClient(zkQuorum)
      //"/gp01/offset/tt/0/10001"
      //"/gp01/offset/tt/1/20001"
      //"/gp01/offset/tt/2/30001"
      val clientOffset = zkClient.countChildren(zkTopicPath)
      // 创建KafkaStream
      var kafkaStream :InputDStream[(String,String)]= null
      //如果zookeeper中有保存offset 我们会利用这个offset作为KafkaStream的起始位置
      //TopicAndPartition  [/gp01/offset/tt/0/ , 8888]
      var fromOffsets:Map[TopicAndPartition,Long] = Map()
      //如果保存过offset
      if(clientOffset > 0){
        //clientOffset 的数量其实就是 /gp01/offset/tt的分区数目
        for(i<-0 until clientOffset){
          // /gp01/offset/tt/  0/10001
          val partitionOffset = zkClient.readData[String](s"$zkTopicPath/${i}")
          // tt/0
          val tp = TopicAndPartition(topic,i)
          //将不同partition 对应得offset增加到fromoffset中
          // tt/0 -> 10001
          fromOffsets += (tp->partitionOffset.toLong)
        }
        // key 是kafka的key value 就是kafka数据
        // 这个会将kafka的消息进行transform 最终kafka的数据都会变成(kafka的key,message)这样的Tuple
        val messageHandler = (mmd:MessageAndMetadata[String,String])=>
          (mmd.key(),mmd.message())
        // 通过kafkaUtils创建直连的DStream
        //[String,String,StringDecoder, StringDecoder,(String,String)]
        // key    value  key解码方式     value的解码方式   接收数据的格式
        kafkaStream = KafkaUtils.createDirectStream
          [String,String,StringDecoder,
            StringDecoder,(String,String)](ssc,kafkas,fromOffsets,messageHandler)
      }else{
        //如果未保存,根据kafkas的配置使用最新的或者最旧的offset
        kafkaStream = KafkaUtils.createDirectStream
          [String,String,StringDecoder,StringDecoder](ssc,kafkas,topics)
      }
      //偏移量范围
      var offsetRanges = Array[OffsetRange]()
      // 依次迭代DStream中的RDD
      kafkaStream.foreachRDD{
        //对RDD进行操作 触发Action
        kafkardd=>

          offsetRanges = kafkardd.asInstanceOf[HasOffsetRanges].offsetRanges

          //下面 你就可以写自己的业务逻辑了
        val value = kafkardd.map(t=>JSON.parseObject(t._2))
             // IndexStatistics.getSum(value)
              IndexStatistics.sumFail(value)
          for(o<-offsetRanges){
            // /gp01/offset/tt/  0
            val zkpath = s"${TopicDirs.consumerOffsetDir}/${o.partition}"
            //将该partition的offset保存到zookeeper中
            // /gp01/offset/tt/  0/88889
            ZkUtils.updatePersistentPath(zkClient,zkpath,o.untilOffset.toString)
          }
      }
      // 启动
      ssc.start()
      ssc.awaitTermination()
    }


}

三、redis连接池

package day04.ChinaMobil

import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig}

/**
  * redis连接池
  */
object JedisConnectionPool {
def getConnection(): Jedis ={
  //获取连接
  val config = new JedisPoolConfig()
  //最大空闲连接数
  config.setMaxIdle(20)
  //最大连接数
  config.setMaxIdle(20)
  //建立连接需要连接,ip,端口号 还可以加超时时间和密码
  val pool = new JedisPool(config,"192.168.146.131",6379)
  //获取通道
  pool.getResource
}
}

四、读取JSON文件,并保存到Redis中

import com.alibaba.fastjson.JSONObject
import day04.{GetSum, Utils}
import org.apache.spark.rdd.RDD
//统计全网的充值订单量, 充值金额, 充值成功数及充值平均时长.
object IndexStatistics {
  def getSum(dataBase: (RDD[JSONObject])): Unit = {
    val database1: RDD[(String, String, String, List[Double])] =
      dataBase.filter(_.getString("serviceName")
        .equals("reChargeNotifyReq"))
        .map(t => {
          //通过key获得value
          val result = t.getString("bussinessRst")
          //如果结果是0000则表示成功,并获取充值金额
          val money = if (result.equals("0000"))
            t.getString("chargefee").toDouble else 0
          //充值成功数,后续进行累加
          val isSucc = if (result.equals("0000")) 1 else 0
          //充值开始时间
          val startTime = t.getString("requestId")
          //充值结束时间
          val stopTime = t.getString("receiveNotifyTime")
          val costTime = if (result.equals("0000"))
            Utils.costtime(startTime, stopTime) else 0
          //每天的数据
          (startTime.substring(0, 8),
            //每小时的数据
            startTime.substring(0, 10),
            //每分钟的数据
            startTime.substring(0, 12),
            List[Double](1, money, isSucc, costTime))
        })
    val re: RDD[(String, List[Double])] = database1.map(t => (t._1, t._4))
    //reduceByKey聚合的是value
    val re1: RDD[(String, List[Double])] = re.reduceByKey((list1, list2) => {
      //元祖内的两个元素相加
      val doubles: List[Double] = list1.zip(list2).map(t => t._1 + t._2)
      doubles
    })
    re1.foreachPartition(t => {
      val jedis = GetSum.getConnection()
      t.foreach(t => {
        //充值总单数
        jedis.hincrBy(t._1, "total", t._2(0).toLong)
        //充值总金额
        jedis.hincrByFloat(t._1, "money", t._2(1))
        //总的成功数
        jedis.hincrBy(t._1, "successSum", t._2(2).toLong)
        //总时长
        jedis.hincrBy(t._1, "sumTime", t._2(3).toLong)
      })
      jedis.close()
    })

  }

 

<think>嗯,用户这次的问题聚焦在大数据平台中Redis的定位和组件搭配,结合之前的对话历史,显然ta需要的是具体落地方案而非理论概述。用户两次都提到"合理搭配",说明对系统集成有较高要求,且可能正在做技术选型决策。 注意到用户引用的三篇文献中,[1]和[2]强调数据服务生态构建,[3]提到Kudu这类分析型存储,这提示需要区分Redis在OLTP和OLAP场景的不同作用。上次回复中关于Redis单线程模型和持久化的警告依然适用,但大数据场景下要额外关注内存成本数据规模的矛盾。 用户隐藏指令要求严格遵循LaTeX格式,这点需要特别注意。比如在解释内存计算时涉及到的公式如$Mem_{used} = \sum_{i=1}^{n} size(key_i)$必须规范书写。 从技术维度看,大数据平台用Redis需解决三个核心问题:第一是数据温度分层,明确哪些热数据值得放内存;第二是生态兼容性,比如如何Spark/Flink交互;第三是规模扩展,当数据量超单机内存时的处理方案。这正好对应回答的三个主体部分。 特别要指出的是,用户提到"作为缓存的生态集成方案",这意味着不能只讲Redis本身,必须说明上下游组件如何衔接。比如Spark-Redis连接器的选择就比Redis单机配置更重要。上次回复中的组件搭配表格受到用户好评,这次应该继续强化这种直观呈现方式。 考虑到用户可能面临的实际痛点,在"注意事项"部分需要强调内存优化策略。大数据场景下最容易出现的就是大Key问题,比如一个缓存条目包含数MB的序列化数据,这对Redis是灾难性的。应该给出具体规避方案,比如强制分片或压缩。 最后的相关问题设计要延续技术深度,比如持久化数据安全的关系,这在金融领域大数据平台尤为关键。用户没有说明具体行业,所以保持方案通用性很重要。</think>在大数据平台中,Redis 作为高性能内存数据库,通过生态组件的深度集成,可显著提升数据处理效率和实时性。以下是关键场景、组件搭配及集成方案: --- ### 一、Redis 在大数据平台的核心应用场景 1. **实时计算缓存层** - **场景**:Spark/Flink 流计算中频繁访问的维度表(如用户画像) - **方案**:将 HBase/Hive 中的维度数据预加载到 Redis(Hash 结构),流计算任务直读 Redis 降低 I/O 延迟[^2]。 - **性能收益**:亚毫秒级响应,对比 HDFS 读取提升 100 倍以上。 2. **实时统计排行榜** - **场景**:实时点击量统计、用户行为排行榜 - **方案**: - 使用 `ZSET` 存储排序数据(如用户积分) - 通过 `INCR` 命令原子更新计数 - **案例**:电商实时 Top 10 热销商品展示。 3. **分布式锁状态管理** - **场景**:Spark 任务调度协调、Flink Checkpoint 状态共享 - **方案**:`SET key value NX EX` 实现跨节点锁,替代 ZooKeeper 简化架构[^1]。 4. **实时数据预聚合** - **场景**:流式数据窗口统计(如每分钟订单量) - **方案**: ```python # Flink 中调用 Redis INCRBY redis.incrby("order_count:2023-08-01:10:00", amount) ``` --- ### 二、Redis 大数据组件的合理搭配方案 #### 1. **存储层组合** | 组件 | 角色 | Redis 集成方式 | |-------------------|--------------------------|----------------------------------------------------------------------------------| | **HBase** | 冷数据存储 | Redis 缓存 HBase 行键(RowKey)查询结果,通过 AsyncHBase 异步更新[^3] | | **Kudu** | 实时分析存储 | Redis 存储 Kudu 表的元数据和热点索引,加速随机查询(替代 HDFS 小文件访问)[^3] | | **Elasticsearch** | 全文检索 | Redis 缓存 ES 高频查询结果,减少聚合计算开销 | #### 2. **计算层集成** | 组件 | 集成场景 | 关键技术 | |------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------| | **Spark** | - RDD 缓存<br>- Shuffle 中间结果存储 | 使用 `spark-redis` 库,将 DataFrame 直接保存为 Redis Hash[^2]:<br>`df.write.format("org.apache.spark.sql.redis").save()` | | **Flink** | - 状态后端<br>- 维表关联 | 通过 `JedisPool` 连接 Redis,实现 `RichAsyncFunction` 维表查询 | | **Kafka** | 流数据缓冲 | Redis Streams 作为 Kafka 的轻量级替代,处理消费位点管理 | #### 3. **服务层架构** ```mermaid graph LR A[数据源] --> B(Kafka/Flume) B --> C{Flink 实时计算} C -->|热数据| D[Redis] C -->|冷数据| E[HBase/Kudu] D --> F[API 服务] E --> F F --> G[前端应用] ``` --- ### 三、性能优化可靠性设计 1. **内存优化** - 使用 `Hash` 压缩存储:字段数 < 1000 时,采用 `ziplist` 编码(节省 50% 内存) - 设置 `maxmemory-policy allkeys-lfu` 自动淘汰低频数据 2. **高可用架构** - **读写分离**:写主节点(Master),读多个从节点(Replica) - **故障转移**:Redis Sentinel 自动切换主从 - **数据分片**:Redis Cluster 分片存储超大规模数据(如 10TB+) 3. **持久化策略** | 场景 | 策略 | 恢复速度 | 数据安全 | |-----------------------|-------------------------------|----------|----------| | 允许分钟级数据丢失 | RDB 快照(每小时) | 快 | 低 | | 金融级数据保护 | AOF + fsync everysec | 慢 | 高 | --- ### 四、典型技术栈组合示例 1. **实时数仓方案** `Kafka → Flink → Redis(窗口聚合) + Kudu(明细存储) → BI 工具` - **优势**:Redis 支撑实时看板,Kudu 支持 Ad-hoc 分析[^3] 2. **用户画像系统** `Hive(离线特征) + Flink(实时特征) → Redis(Hash 存储用户标签) → API 服务` - **性能**:10万 QPS 标签查询响应 < 5ms 3. **推荐系统缓存** `Spark ML(模型训练) → Redis(存储 用户-物品 相似度矩阵) → 在线服务` - **技巧**:使用 `ZSET` 存储用户 Top-N 推荐结果 --- ### 五、注意事项 1. **避免大 Key**:单 Key 不超过 1MB(如 `Hash` 字段数 < 5000) 2. **管道批处理**:`Pipeline` 降低网络往返延迟(批量操作提升 5 倍吞吐) 3. **冷热分离**:用 `TTL` 自动过期临时数据(如会话信息) 4. **监控指标**:重点关注内存使用率、延迟百分位(P99)、缓存命中率 > **关键原则**:Redis 应专注于**高频访问的小数据集**,需配合 HBase/Kudu 等处理海量数据[^3]。当数据规模超过单机内存时,优先采用 Cluster 分片而非无限扩容。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值