文章目录
- 原理
- 关于 *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,在了解上面概念后我们直接上代码看下重写监听的方式吧
重写监听
- 开启redis key变化事件
redis配置文件配置notify-keyspace-events AKEx
,默认是关闭的
- 引入依赖(其他正常的依赖省略了,记得添加哈)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
- 写个
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;}
}
- 写个
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等文件类型互转
我们继续看下容器注册方式
容器注册
上面的配置就不重写了
- 注意我们写个
RedisKeyUpdatedListener
implementsMessageListener
@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));}
}
- 然后修改下容器配置
@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。优雅封装接口给第三方调用