SpringBoot整合 redis key (过期、新增、修改)的三种方式,看这篇就够了

文章目录

  • 原理
  • 关于 *notify-keyspace-events*
  • 关于redis的消息主题(Topic)
  • 重写监听
  • 容器注册
  • 自定义解析
  • 常见整合问题
  • 鸣谢

文章主要描述了Springboot整合key变化的三种方式,同时列出了一些整合坑点与概念

原理

SpringBoot整合Redis key变化的原理就是万变不离其宗,简单点就是:
spring-boot-starter-data-redis + notify-keyspace-events

关于 notify-keyspace-events

  • notify-keyspace-events AKEx 是 Redis 中的一个命令,用于配置服务器发送的通知类型。这里的参数 AKEx 代表的是键空间通知和键事件通知。

  • 具体来说,A 表示接收所有类型的通知,K 表示接收键空间通知,Ex 表示接收键事件通知。键空间通知是关于整个数据库的变化,而键事件通知是关于特定键的变化。

  • 举个例子,如果你在 Redis 中执行了 SET mykey "Hello" 命令,那么使用了 notify-keyspace-events AKEx 配置的客户端将会接收到一个关于 mykey 键的键事件通知。

  • 需要注意的是,要使用 notify-keyspace-events 命令,必须在 Redis 服务器中启用相关的 notify 机制,否则客户端无法接收到任何通知。同时,该命令只能在 Redis 的 string 类型键的值被修改时才会发送通知,其他类型键(如 hash、list、set、sorted set 等)的修改不会触发通知。

关于redis的消息主题(Topic)

Redis 消息主题(Topic)是用于发布和订阅消息的关键字。根据 Redis 的设计,它只支持发布/订阅模型的消息,而不支持请求/响应模型的消息。

在 Redis 中,可以使用 PSUBSCRIBE 命令来订阅一个或多个主题,并监听相关的消息。以下是一些常见的 Redis 消息主题:

  • __keyevent@*__:expired:过期事件主题,发布所有库过期消息,*表示所有。
  • __keyevent@0__:expired:过期事件主题,发布db0库过期消息0代表db0
  • __keyevent@1__:del:当使用 DEL 命令删除一个键时触发的事件。
  • __keyevent@4__:rename:当使用 RENAME 命令重命名一个键时触发的事件。
  • __keyevent@5__:set:当使用 SET 命令设置一个键的值时触发的事件。

这些主题是在 Redis 4.0 版本中引入的,用于表示键的特定事件。通过订阅相应的主题,可以监听相关的事件并进行相应的处理。
除了以上列举的主题外,Redis 还支持自定义的主题,用户可以根据自己的需求来定义和订阅相关的主题。

ok,在了解上面概念后我们直接上代码看下重写监听的方式吧

重写监听

  1. 开启redis key变化事件
    redis配置文件配置 notify-keyspace-events AKEx ,默认是关闭的
  2. 引入依赖(其他正常的依赖省略了,记得添加哈)
<dependency>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-data-redisartifactId>
 dependency>
  1. 写个RedisListenerConfig 配置文件(文件名随便取,不一定要跟博主一样)


@Configuration
public class RedisListenerConfig {
	//配置redis监听容器
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
    //配置redis的序列化策略
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//所有属性均可见
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);//为null不参加序列化
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//在Redis中存储对象类信息
        jackson2JsonRedisSerializer.setObjectMapper(mapper);
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.setConnectionFactory(factory);
        return template;
    }
}
  1. 写个RedisKeyExpirationListener监听器
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

    public RedisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) {
        super(redisMessageListenerContainer);
    }

    /**
     * 针对redis数据失效事件,进行数据处理
     * @param message message must not be {@literal null}. 过期的key
     * @param pattern pattern matching the channel (if specified) - can be {@literal null}. 队列名称
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 拿到key
        String expiredKey = message.toString();
        log.info("监听Redis key过期,key:{},channel:{}", expiredKey, new String(pattern));
    }
}

到此就完成了key过期监听,那这种方式怎么做key更新和删除呢

KeyExpirationEventMessageListener 该类是Springboot封装的过期监听类,我们看下源码。

SpringBoot整合 redis key (过期、新增、修改)的三种方式,看这篇就够了_第1张图片

但是Springboot可没有跟你封装更新和删除的哦。所以我们要学会举一反三。

  • 第一步复制KeyExpirationEventMessageListener,假设名字是KeyUpdateEventMessageListener
  • 修改__keyevent@0__:expired__keyevent@0__:set 这个是更新的topic
  • 写个RedisKeyUpdateListener extent KeyUpdateEventMessageListener,重写onMessage方法

ok,写完了,插个tips。Python开发的文件类型转化windows工具,支持png,jpeg,ico等文件类型互转

我们继续看下容器注册方式

容器注册

上面的配置就不重写了

  1. 注意我们写个 RedisKeyUpdatedListener implements MessageListener
@Component
@Slf4j
public class RedisKeyUpdatedListener implements MessageListener {

    /**
     * key 更新监听
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 拿到key
        String expiredKey = message.toString();
        log.info("监听Redis keyg更新,key:{},channel:{}", expiredKey, new String(pattern));
    }
}
  1. 然后修改下容器配置
@Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //监听指定db0 的key set事件 *表示监听所有的db
        container.addMessageListener(redisKeyUpdatedListener, new PatternTopic("__keyevent@*__:set"));
        return container;
    }

这里需要注意下redisKeyUpdatedListener 是通过注入方式注入的,不是new,因为如果通过new的方式。监听器有业务逻辑时会引入多个业务service组件。通过new的方式就只能通过构造的方式传入,而且service组件是从配置类注入的。

多个的话如下

container.addMessageListener(redisKeyUpdatedListener, new PatternTopic("__keyevent@*__:set"));
container.addMessageListener(redisKeyExpiredListener, new PatternTopic("__keyevent@*__:expired"));
container.addMessageListener(redisKeyDelListener, new PatternTopic("__keyevent@*__:del"));

自定义解析

这个就比较方便了。直接看代码吧,但耦合度有点高,不符合设计模式规范

@Component
public class CustomRedisMessageListener implements MessageListener {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String ops = new String(message.getBody());     //操作
        String channel = new String(message.getChannel());
        String key = channel.split(":")[1];
		//对redis的新增或删除事件进行监听
        if ("set".equals(ops)) {
            String value = redisTemplate.opsForValue().get(key);
            handleSet(key, value);
        } else if ("del".equals(ops)) {
            handleDel(key);
        }
    }

    /**
     * 监听新增 处理逻辑
     */
    private void handleSet(String key, String value) {
			//将数据同步刷新到内存中
            gatewayCache.refreshApiWhitelistsCache(id, JsonUtil.toObject(value, Set.class));
    }

    /**
     * 监听删除 处理逻辑
     * @param key 被删除的key
     */
    private void handleDel(String key) { 
            gatewayCache.deleteApiWhitelists(id);
    }
}

常见整合问题

为啥我引入包,代码也无比的正确,为啥key过期了就是监听不到呢

  • 确保项目能启动
  • 确保依赖是否冲突了
  • 确保redis配置开启了 notify-keyspace-events AKEx

为啥我配置了notify-keyspace-events 。就是没有监听到key 更新事件呢?

  • 确保配置的是 notify-keyspace-events AKEx,而不是 notify-keyspace-events Ex。ex只能监听到过期事件,而监听不到删除事件

鸣谢

  • 非常感谢你从头到尾阅读了这篇文章,希望其中的内容对你有所启发和帮助。如果你还有其他问题或需要进一步的了解,欢迎随时关注我的动态并留言
  • 最后希望大家给作者点个关注和小赞赞支持下,创作不易啊
  • 觉得有收藏价值也可以进行收藏
  • 最后给大家来波小tips。优雅封装接口给第三方调用

你可能感兴趣的:(spring,boot,redis)