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

news/2024/10/20 15:56:22/

文章目录

  • 原理
  • 关于 *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.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
  1. 写个RedisListenerConfig 配置文件(文件名随便取,不一定要跟博主一样)
@Configuration
public class RedisListenerConfig {//配置redis监听容器@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);return container;}//配置redis的序列化策略@Beanpublic 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}. 队列名称*/@Overridepublic void onMessage(Message message, byte[] pattern) {// 拿到keyString expiredKey = message.toString();log.info("监听Redis key过期,key:{},channel:{}", expiredKey, new String(pattern));}
}

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

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

在这里插入图片描述

但是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 更新监听*/@Overridepublic void onMessage(Message message, byte[] pattern) {// 拿到keyString expiredKey = message.toString();log.info("监听Redis keyg更新,key:{},channel:{}", expiredKey, new String(pattern));}
}
  1. 然后修改下容器配置
@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);//监听指定db0 的key set事件 *表示监听所有的dbcontainer.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 {@Autowiredprivate StringRedisTemplate redisTemplate;@Overridepublic 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。优雅封装接口给第三方调用

http://www.ppmy.cn/news/1069292.html

相关文章

JVM 访问对象的两种方式

Java 程序会通过栈上的 reference 数据来操作堆上的具体对象。由于 reference 类型在《Java 虚拟机规范》里面只规定了它是一个指向对象的引用&#xff0c;并没有定义这个引用应该通过什么方式去定位、访问到堆中对象的具体位置&#xff0c;所以对象访问方式也是由虚拟机实现而…

数据库第十七课-------ETL任务调度系统的安装和使用

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

②matlab桌面和编辑器

目录 matlab编辑器练习 运行脚本 matlab编辑器练习 您可以通过点击灰色代码框在脚本中输入命令。 准备就绪后&#xff0c;您可以通过点击蓝色的提交按钮提交代码。 任务 在脚本中输入命令 r 3。 2.任务 在脚本中添加命令 x pi*r^2。 附加练习 当您在实时编辑器中完成…

排序算法-选择排序(Java)

选择排序 选择排序 &#xff08;selection sort&#xff09;的工作原理非常直接&#xff1a;开启一个循环&#xff0c;每轮从未排序区间选择最小的元素&#xff0c;将其放到已排序区间的末尾。 算法原理 排序数组&#xff1a;&#xff08;2 4 3 1 5 2&#xff09; &#xf…

pyqt5-快捷键QShortcut

import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import *""" 下面示例揭示了&#xff0c;当关键字绑定的控件出现的时候&#xff0c;快捷键才管用&#xff0c; 绑定的控件没有出现的时候快捷键无效 """…

C# 序列化json数据,datatabel转对象

datatabel直接转对象 转对象逻辑 1.将datatabel转为json格式 2.将json格式的内容转化为模型data_model的list对象 JsonConvert.DeserializeObject<List<data_model>>(JsonConvert.SerializeObject(dt))

大厂数仓模型规范与度量指标有哪些?

在数仓建设中&#xff0c;模型质量评价体系是一种重要的方法&#xff0c;用于评估数据模型的规范程度、数据质量和可信度。随着数据驱动的决策在企业中的重要性日益增加&#xff0c;数据仓库作为数据沟通和业务系统之间的中介&#xff0c;扮演着关键的角色。因此&#xff0c;确…

Eclipse打jar包与JavaDOC文档的生成

补充知识点——Eclipse打jar包与JavaDOC文档的生成 1、Eclipse如何打jar包&#xff0c;如何运行jar包 Java当中编写的Java代码&#xff0c;Java类、方法、接口这些东西就是项目中相关内容&#xff0c;到时候我们需要把代码提供给甲方、或者是我们需要运行我们编写的代码&…