Redis的键空间监听功能

文章目录

Redis 键空间通知

一、keyspace介绍

keyspace(键空间通知)针对指定key发生的一切改动,推送给订阅的客户端,侧重于针对指定key的操作

键空间通知监听格式:__keyspace@<db>__:<key>

二、事件通知配置

默认情况下,键空间事件通知被禁用,因为虽然不太明智,但该功能会使用一些 CPU 功率。notify-keyspace-events有两种配置方式

  • 通过redis.conf配置
  • 通过config set 启用通知,重启失效
bash 复制代码
例:config set notify-keyspace-events "KEA"

将参数设置为空字符串会禁用通知。为了启用该功能,使用由多个字符组成的非空字符串,其中每个字符都有特殊含义

字符 发送的通知
K 键空间通知,所有通知以 __keyspace@<db>__ 为前缀
E 键事件通知,所有通知以__keyevent@<db>__为前缀
g delexpirerename 等类型无关的通用命令的通知
$ string命令的通知
l list命令的通知
s set命令的通知
h hash命令的通知
z zset命令的通知
e 驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送
A 参数 g$lshzxe 的别名

至少KE应该出现在字符串中,否则无论字符串的其余部分如何,都不会传递任何事件。

例如,要仅启用列表的键空间事件,配置参数必须设置为Kl,等等。

您可以使用该字符串KEA来启用所有类型的通知

三、不同命令生成的事件

具体的看官方文档:Redis 键空间通知,这里举几个例子:

  • DEL为每个已删除的键生成一个del事件。
  • RENAME生成两个事件,一个rename_from针对源键的事件,一个rename_to针对目标键的事件。
  • MOVE生成两个事件,一个move_from针对源键的事件,一个move_to针对目标键的事件。
  • COPY生成一个copy_to事件。
  • EXPIRE及其所有变体(PEXPIREEXPIREATPEXPIREATexpire在使用正超时(或未来时间戳)调用时都会生成一个事件。请注意,当使用过去的负超时值或时间戳调用这些命令时,键将被删除,并且仅del生成一个事件。
  • ...

四、客户端测试

Redis键空间监听的本质是通过订阅与键相关的消息通知来实现的。当一个Redis键空间的事件发生时,Redis会将事件信息发送到相应的频道(channel),而订阅了该频道的客户端就会接收到这些消息通知。

1.打开redis客户端连接,然后设置所有类型事件都通知

bash 复制代码
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:*就会触发通知

bash 复制代码
# 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.新开一个客户端

bash 复制代码
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.引入依赖

xml 复制代码
<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

yaml 复制代码
server.port=8080

spring.redis.port=6379
spring.redis.host=127.0.0.1

3.配置监听器

java 复制代码
@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.开启监听以及配置监听范围

java 复制代码
@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配置

java 复制代码
@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,在项目启动时动态注入容器

java 复制代码
@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.结果一样

相关推荐
Leo.yuan41 分钟前
数据量大Excel卡顿严重?选对报表工具提高10倍效率
数据库·数据分析·数据可视化·powerbi
Runing_WoNiu1 小时前
MySQL与Oracle对比及区别
数据库·mysql·oracle
sam-1231 小时前
k8s上部署redis高可用集群
redis·docker·k8s
天道有情战天下1 小时前
mysql锁机制详解
数据库·mysql
看山还是山,看水还是。1 小时前
Redis 配置
运维·数据库·redis·安全·缓存·测试覆盖率
谷新龙0011 小时前
Redis运行时的10大重要指标
数据库·redis·缓存
CodingBrother1 小时前
MySQL 中单列索引与联合索引分析
数据库·mysql
精进攻城狮@1 小时前
Redis缓存雪崩、缓存击穿、缓存穿透
数据库·redis·缓存
小酋仍在学习2 小时前
光驱验证 MD5 校验和
数据库·postgresql
keep__go2 小时前
Linux 批量配置互信
linux·运维·服务器·数据库·shell