文章主要描述了Springboot整合key变化的三种方式,同时列出了一些整合坑点与概念
SpringBoot整合Redis key变化的原理就是万变不离其宗,简单点就是:
spring-boot-starter-data-redis + 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 的设计,它只支持发布/订阅模型的消息,而不支持请求/响应模型的消息。
在 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,在了解上面概念后我们直接上代码看下重写监听的方式吧
notify-keyspace-events AKEx
,默认是关闭的
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
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;
}
}
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可没有跟你封装更新和删除的哦。所以我们要学会举一反三。
KeyExpirationEventMessageListener
,假设名字是KeyUpdateEventMessageListener
__keyevent@0__:expired
为__keyevent@0__:set
这个是更新的topicRedisKeyUpdateListener extent KeyUpdateEventMessageListener
,重写onMessage
方法ok,写完了,插个tips。Python开发的文件类型转化windows工具,支持png,jpeg,ico等文件类型互转
我们继续看下容器注册方式
上面的配置就不重写了
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));
}
}
@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过期了就是监听不到呢
notify-keyspace-events AKEx
为啥我配置了notify-keyspace-events 。就是没有监听到key 更新事件呢?
notify-keyspace-events AKEx
,而不是 notify-keyspace-events Ex
。ex只能监听到过期事件,而监听不到删除事件