文章目录
- Redis 键空间通知
- 一、keyspace介绍
- 二、事件通知配置
- 三、不同命令生成的事件
- 四、客户端测试
- 五、Springboot整合Redis键空间监听
- 5.1 方式一
- 5.2 方式二
Redis 键空间通知
一、keyspace介绍
keyspace(键空间通知)针对指定key发生的一切改动,推送给订阅的客户端,侧重于针对指定key的操作
键空间通知监听格式:__keyspace@<db>__:<key>
二、事件通知配置
默认情况下,键空间事件通知被禁用,因为虽然不太明智,但该功能会使用一些 CPU 功率。notify-keyspace-events
有两种配置方式
- 通过
redis.conf
配置 - 通过
config set
启用通知,重启失效
例:config set notify-keyspace-events “KEA”
将参数设置为空字符串会禁用通知
。为了启用该功能,使用由多个字符组成的非空字符串,其中每个字符都有特殊含义
字符 | 发送的通知 |
---|---|
K | 键空间通知,所有通知以 __keyspace@<db>__ 为前缀 |
E | 键事件通知,所有通知以__keyevent@<db>__ 为前缀 |
g | del 、expire 、rename 等类型无关的通用命令的通知 |
$ | string 命令的通知 |
l | list 命令的通知 |
s | set 命令的通知 |
h | hash 命令的通知 |
z | zset 命令的通知 |
e | 驱逐(evict )事件:每当有键因为 maxmemory 政策而被删除时发送 |
A | 参数 g$lshzxe 的别名 |
至少K
或E
应该出现在字符串中,否则无论字符串的其余部分如何,都不会传递任何事件。
例如,要仅启用列表的键空间事件,配置参数必须设置为Kl,等等。
您可以使用该字符串KEA
来启用所有类型的通知
三、不同命令生成的事件
具体的看官方文档:Redis 键空间通知,这里举几个例子:
DEL
为每个已删除的键生成一个del
事件。RENAME
生成两个事件,一个rename_from
针对源键的事件,一个rename_to
针对目标键的事件。MOVE
生成两个事件,一个move_from
针对源键的事件,一个move_to
针对目标键的事件。COPY
生成一个copy_to
事件。EXPIRE
及其所有变体(PEXPIRE
、EXPIREAT
、PEXPIREAT
)expire
在使用正超时(或未来时间戳)调用时都会生成一个事件。请注意,当使用过去的负超时值或时间戳调用这些命令时,键将被删除,并且仅del
生成一个事件。- …
四、客户端测试
Redis键空间监听的本质是通过订阅与键相关的消息通知来实现的。当一个Redis键空间的事件发生时,Redis会将事件信息发送到相应的频道(channel),而订阅了该频道的客户端就会接收到这些消息通知。
1.打开redis客户端连接,然后设置所有类型事件都通知
C:\Users\Lenovo>redis-cli
127.0.0.1:6379> config set notify-keyspace-events "KEA"
OK
127.0.0.1:6379>
2.订阅一个频道,配置key为qqq:*
就会触发通知
# PSUBSCRIBE命令用于订阅一个或多个符合指定模式的频道(channel),支持通配符模式
127.0.0.1:6379> PSUBSCRIBE __keyspace@*__:qqq:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyspace@*__:qqq:*"
3) (integer) 1
3.新开一个客户端
C:\Users\Lenovo>redis-cli
127.0.0.1:6379> set qqq:111 222
OK
127.0.0.1:6379>
结果:可以看到只要qqq:*
的发生了变动,就会触发通知
五、Springboot整合Redis键空间监听
5.1 方式一
1.引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2.配置redis
server.port=8080
spring.redis.port=6379
spring.redis.host=127.0.0.1
3.配置监听器
@Slf4j
@Component
public class RedisListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.println(message.toString());
String body = new String(message.getBody());
System.out.println(body);
String channel = new String(message.getChannel());
System.out.println(channel);
int index = channel.indexOf(":");
String key = channel.substring(index + 1); // 找到第一个冒号的位置,冒号后面就是key
log.info("redis key: {} , channel: {}", key, channel);
}
}
4.开启监听以及配置监听范围
@Component
public class RedisContainer {
@Autowired
RedisListener redisListener;
@Autowired
private TaskExecutor redisListenerContainerTaskExecutor;
private static final Topic TOPIC_ALL_KEYSPACE = new PatternTopic("__keyspace@*__:*");
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory factory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
// __keyspace@*__:* 可以配置一个也可配置多个
container.addMessageListener(redisListener, TOPIC_ALL_KEYSPACE);
container.setTaskExecutor(redisListenerContainerTaskExecutor);
return container;
}
}
5.测试
5.2 方式二
1.增加redisTemplate配置
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}
2.使用@PostConstruct,在项目启动时动态注入容器
@Slf4j
@Component
public class service {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private TaskExecutor redisListenerContainerTaskExecutor;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final Topic TOPIC_ALL_KEYSPACE = new PatternTopic("__keyspace@*__:*");
@PostConstruct
void init(){
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
// 创建redis监听器
MessageListener listener = (message, pattern) -> {
System.out.println("message: " + message.toString());
String body = new String(message.getBody());
System.out.println("body: " + body);
String channel = new String(message.getChannel());
System.out.println("channel: " + channel);
int index = channel.indexOf(":");
String key = channel.substring(index + 1); // 找到第一个冒号的位置,冒号后面就是key
log.info("redis key: {} , channel: {}", key, channel);
};
// 创建redis监听适配器
MessageListenerAdapter listenerAdapter = new MessageListenerAdapter(listener);
// 创建redis消息监听容器
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisTemplate.getConnectionFactory());
container.addMessageListener(listenerAdapter, TOPIC_ALL_KEYSPACE);
container.setTaskExecutor(redisListenerContainerTaskExecutor);
// redis消息监听容器注入到spring中
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RedisMessageListenerContainer.class, () -> container);
defaultListableBeanFactory.registerBeanDefinition("redisMessageListenerContainer", listenerBeanDefinitionBuilder.getBeanDefinition());
}
}
3.结果一样