文章目录
- 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=8080spring.redis.port=6379
spring.redis.host=127.0.0.1
3.配置监听器
@Slf4j
@Component
public class RedisListener implements MessageListener {@Overridepublic 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); // 找到第一个冒号的位置,冒号后面就是keylog.info("redis key: {} , channel: {}", key, channel);}
}
4.开启监听以及配置监听范围
@Component
public class RedisContainer {@AutowiredRedisListener redisListener;@Autowiredprivate TaskExecutor redisListenerContainerTaskExecutor;private static final Topic TOPIC_ALL_KEYSPACE = new PatternTopic("__keyspace@*__:*");@Beanpublic 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 {@Beanpublic 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 {@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate TaskExecutor redisListenerContainerTaskExecutor;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final Topic TOPIC_ALL_KEYSPACE = new PatternTopic("__keyspace@*__:*");@PostConstructvoid 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); // 找到第一个冒号的位置,冒号后面就是keylog.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.结果一样