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.结果一样

相关推荐
.Eyes1 小时前
OceanBase 分区裁剪(Partition Pruning)原理解读
数据库·oceanbase
MrZhangBaby2 小时前
SQL-leetcode— 2356. 每位教师所教授的科目种类的数量
数据库
一水鉴天2 小时前
整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接 之2
数据库·人工智能·智能合约
翔云1234562 小时前
Python 中 SQLAlchemy 和 MySQLdb 的关系
数据库·python·mysql
孙霸天3 小时前
Ubuntu20系统上离线安装MongoDB
数据库·mongodb·ubuntu·备份还原
Java 码农3 小时前
nodejs mongodb基础
数据库·mongodb·node.js
TDengine (老段)3 小时前
TDengine IDMP 运维指南(4. 使用 Docker 部署)
运维·数据库·物联网·docker·时序数据库·tdengine·涛思数据
TDengine (老段)3 小时前
TDengine IDMP 最佳实践
大数据·数据库·物联网·ai·时序数据库·tdengine·涛思数据
Java小混子3 小时前
【Redis】缓存和分布式锁
redis·分布式·缓存
彬彬醤4 小时前
Mac怎么连接VPS?可以参考这几种方法
大数据·运维·服务器·数据库·线性代数·macos·矩阵