开发者

通过Redisson监听Redis集群的Key过期事件的实现指南

开发者 https://www.devze.com 2025-11-10 08:57 出处:网络 作者: 非ban必选
价值2999元 Java视频教程限时免费下载
专为Java开发者设计,涵盖核心技术、架构设计、性能优化等
立即下载
目录引言一、前置条件:开启 Redis 集群的键过期通知1. 修改配置文件2. 生效配置二、引入 Redisson 依赖三、配置 Redisson 连接 Redis 集群四、实现 Key 过期事件监听器方式 1:监听指定数据库的过期事件(精确订阅)
目录
  • 引言
  • 一、前置条件:开启 Redis 集群的键过期通知
    • 1. 修改配置文件
    • 2. 生效配置
  • 二、引入 Redisson 依赖
    • 三、配置 Redisson 连接 Redis 集群
      • 四、实现 Key 过期事件监听器
        • 方式 1:监听指定数据库的过期事件(精确订阅)
        • 方式 2:监听所有数据库的过期事件(通配符订阅)
      • 五、关键技术细节与最佳实践
        • 1. 为什么必须使用 StringCodec?
        • 2. 如何保证事件处理的可靠性?
        • 3. 性能与集群适配注意事项

      引言

      在分布式系统中,监听 Redis 键过期事件是实现缓存失效、订单超时取消等功能的常用方案。本文将详细介绍如何通过 Redisson 高效监听 Redis 集群的 Key 过期事件,包含环境配置、代码实现及最佳实践,适用于生产环境落地。

      一、前置条件:开启 Redis 集群的键过期通知

      Redis 默认关闭键事件通知,需在所有集群节点的配置文件(redis.conf)中开启过期事件监听,确保事件能被正确触发:

      1. 修改配置文件

      # 开启键过期事件通知(E表示键事件,x表示过期事件)
      notify-keyspace-events Ex
      • 配置说明:notify-keyspace-events参数用于指定监听的事件类型,Ex组合表示仅监听键过期事件(减少不必要的事件推送,提升性能)。

      2. 生效配置

      • 若 Redis 已启动,可通过命令临时生效(重启后失效):
      # 连接任意集群节点执行
      redis-cli -h 192.168.1.1 -p 6379 config set notify-keyspace-events Ex
      • 生产环境建议修改配置文件后重启集群,确保配置持久化。

      二、引入 Redisson 依赖

      Redisson 提供了对 Redis 集群的原生支持,通过 Spring Bootjs Starter 可快速集成:

      <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson-spring-boot-starter</artifactId>
          <version>3.52.0</version>
      </dependency>

      三、配置 Redisson 连接 Redis 集群

      Redisson 通过Config类配置集群连接,需指定节点地址、认证信息及连接池参数,确保高可用:

      import org.redisson.Redisson;
      import org.redisson.api.RedissonClient;
      import org.redisson.config.Config;
      import org.redisson.config.ReadMode;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
       
      @Confhttp://www.devze.comiguration
      public class RedissonClusterConfig {
       
          @Bean(destroyMethod = "shutdown") // 容器销毁时关闭客户端,释放资源
          public RedissonClient redissonClient() {
              Config config = new Config();
              
              // 配置Redis集群(至少填写一个主节点地址,Redisson会自动发现其他节点)
              config.useClusterServers()
                    .addNodeAddress(
                        "redis://192.168.1.1:6379",
                        "redis://192.168.1.2:6379",
                        "redis://192.168.1.3:6379"
                    )
                    // 认证配置
                    .setPassword("your-redis-password") // 若集群启用密码认证
                    .setUsername("default") // Redis 6.0+支持用户名认证,默认留空
                    
                    // 连接池配置(根据业务压力调整)
                    .setMasterConnectionPoolSize(32) // 主节点连接池大小
                    .setSlaveConnectionPoolSize(16)  // 从节点连接池大小
                    .setIdleConnectionTimeout(30000) // 连接空闲超时(毫秒)
                    
                    // 集群特性配置
                    .setScanInterval(2000) // 集群节点健康检查间隔(毫秒)
                    .setReadMode(ReadMode.SLAVE) // 读操作路由到从节点,减轻主节点压力
                    .setRetryAttempts(3) // 命令执行失败重试次数
                    .setRetryInterval(1000); // 重试间隔(毫秒)
       
              return Redisson.create(config);
          }
      }

      关键配置说明:

      • destroyMethod = "shutdown":确保 Spring 容器销毁时,Redisson 客户端优雅关闭,避免连接泄漏。
      • ReadMode.SLAVE:非过期事件的读操作可路由到从节点,适合读多写少场景。
      • 连接池大小:根据并发量调整,建议主节点连接池不超过 64(避免 Redis 连接数过载)。
      • 配置文件配置如下
      spring:
        datasource:
          druid:
            password: xxxxxxx
            url: jdbc:mysql://127.0.0.1:3306/service?useUnicode=true&allowMultiQueries=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false&allowpublicKeyRetrieval=true
            username: root
        redis:
          redisson:
            file: classpath:redisson-clu.yaml
      • redisson-clu.yaml
      clusterServersConfig:
        idleConnectionTimeout: 10000
        connectTimeout: 10000
        timeout: 3000
        retryAttempts: 3
        retryInterval: 1500
        failedSlaveReconnectionInterval: 3000
        failedSlaveCheckInterval: 60000
        password: xxxxxxx
        subscriptionsPerConnection: 5
        clientName: null
        loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
        subscriptionConnectionMinimumIdleSize: 1
        subscriptionConnectionPoolSize: 50
      php  slaveConnectionMinimumIdleSize: 24
        slaveConnectionPoolSize: 64
        masterConnectionMinimumIdleSize: 24
        masterConnectionPoolSize: 64
        readMode: "SLAVE"
        subscriptionMode: "SLAVE"
        nodeAddresses:
        - "redis://127.0.0.1:6380"
        - "redis://127.0.0.1:6381"
        - "redis://127.0.0.1:6382"
        - "redis://127.0.0.1:6383"
        - "redis://127.0.0.1:6384"
        - "redis://127.0.0.1:6385"
        scanInterval: 1000
        pingConnectionInterval: 0
        keepAlive: false
        tcpNoDelay: false
      threads: 16
      nettyThreads: 32
      codec: !<org.redisson.codec.jsonJacksonCodec> {}
      transportMode: "NIO"

      四、实现 Key 过期事件监听器

      Redisson 提供两种订阅方式:RTopic(指定数据库)和RPatternTopic(通配符订阅),需结合StringCodec解析事件消息(Redis 过期事件的消息为键名字符串)。

      方式 1:监听指定数据库的过期事件(精确订阅)

      适用于仅关注特定数据库(如 DB 7)的场景,减少无关事件干扰:

      package com.yh.service.config;
       
      import org.redisson.api.RPatternTopic;
      import org.redisson.api.RTopic;
      import org.redi编程客栈sson.api.RedissonClwww.devze.comient;
      import org.redisson.api.listener.MessageListener;
      import org.redisson.api.listener.PatternMessageListener;
      import org.redisson.client.codec.StringCodec;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
       
      import Javax.annotation.PostConstruct;
       
      @Component
      public class RedisKeyExpireListener {
       
          @Autowired
          private RedissonClient redissonClient;
          
       
          @PostConstruct
          public void listenExpireEvent1() {
              // 仅监听 DB 7 的键过期事件
              RTopic topic = redissonClient.getTopic("__keyevent@7__:expired", StringCodec.INSTANCE);
       
              // 注册监听器
              topic.addListener(String.class, new MessageListener<String>() {
                  @Override
                  public void onMessage(CharSequence channel, String msg) {
                      //输出过期的key 值
                      System.out.println(msg);
                  }
              });
       
          }
      }

      效果如下

      通过Redisson监听Redis集群的Key过期事件的实现指南

      方式 2:监听所有数据库的过期事件(通配符订阅)

      适用于需要监听集群中所有数据库过期事件的场景,通过通配符*匹配所有数据库:

      package com.yh.service.config;
       
      import org.redisson.api.RPatternTopic;
      import org.redisson.api.RTopic;
      import org.redisson.api.RedissonClient;
      import org.redisson.api.listener.MessageListener;
      import org.redisson.api.listener.PatternMessageListener;
      import org.redisson.client.codec.StringCodec;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
       
      import javax.annotation.PostConstruct;
       
      @Component
      public class RedisKeyExpireListener {
       
          @Autowired
          private RedissonClient redissonClient;
       
       
      //    @PostConstruct
      //    public void listenExpireEvent1() {
      //        // 仅监听 DB 7 的键过期事件
      //        RTopic topic = redissonClient.getTopic("__keyevent@7__:expired", StringCodec.INSTANCE);
      //        // 注册监听器
      //        topic.addListener(String.class, new MessageListener<String>() {
      //            @Override
      //            public void onMessage(CharSequence channel, String msg) {
      //                //输出过期的key 值
      //                System.out.println(msg);
      //            }
      //        });
      //    }
       
       
          @PostConstruct  // 初始化时订阅事件
          public void listenExpireEvent() {
              // 订阅所有数据库的键过期事件(__keyevent@*__:expired)
              RPatternTopic topic = redissonClient.getPatternTopic("__keyevent@*__:expired",StringCodec.INSTANCE);
       
              // 添加消息监听器
              topic.addListener(String.class,new PatternMessageListener<String>() {
                  @Override
                  public void onMessage(CharSequence pattern, CharSequence channel, String msg) {
                      //输出过期的key 值
                      System.out.println(msg);
                  }
              });
          }
      }

      效果如下

      通过Redisson监听Redis集群的Key过期事件的实现指南

      五、关键技术细节与最佳实践

      1. 为什么必须使用 StringCodec?

      Redis 的过期事件消息内容是原始键名字符串(如order:123),而 Redisson 默认使用JsonJacksonCodec(JSON 解析)。若不指定StringCodec,会导致 JSON 解析错误(如JsonParseException: Unrecognized token 'order'),因此必须显式指定字符串编码器。

      2. 如何保证事件处理的可靠性?

      • 异步处理:监听器的onMessage方法运行在 Redisson 的 Netty IO 线程中,需通过线程池异步执行业务逻辑,避免阻塞事件监听。
      • 防止丢失:Redis 事件通知是 “fire and forget” 模式,监听器宕机期间的事件会丢失。关键业务需结合:
        • 定时任务兜底(如每 10 分钟扫描超时订单);
        • 事件持久化(监听到事件后先存入 Redis Stream/Kafka,再消费处理)。
      • 幂等性设计:同一事件可能因集群主从切换被重复推送,需确保业务逻辑幂等(如处理前检查订单状态)。

      3. 性能与集群适配注意事项

      • 集群事件路由:Redis 集群中,键过期事件会在键所在的节点触发,Redisson 会自动订阅所有节点的事件,无需额外配置。
      • 连接池隔离:业务线程池需与 Redisson 的内部线程池隔离,避免业务阻塞影响 Redis 连接。
      • 事件过滤:通过键名前缀(如order:)过滤无关事件,减少无效处理。
      • 监控告警:添加 metrics 监控(如事件总数、处理成功率),结合告警机制及时发现异常。

      以上就是通过Redisson监听Redis集群的Key过期事件的实现指南的详细内容,更多关于Redisson监听Redis Key过期事件的资料请关注编程客栈(www.devze.com)其它相关文章!

      0
      价值2999元 Java视频教程限时免费下载
      专为Java开发者设计,涵盖核心技术、架构设计、性能优化等
      立即下载

      精彩评论

      暂无评论...
      验证码 换一张
      取 消