SpringBoot整合 redis key (过期、新增、修改)的三种方式,看这篇就够了

文章目录

  • 原理
  • 关于 *notify-keyspace-events*
  • 关于redis的消息主题(Topic)
  • 重写监听
  • 容器注册
  • 自定义解析
  • 常见整合问题
  • 鸣谢

文章主要描述了Springboot整合key变化的三种方式,同时列出了一些整合坑点与概念

原理

SpringBoot整合Redis key变化的原理就是万变不离其宗,简单点就是:
spring-boot-starter-data-redis + notify-keyspace-events

关于 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 消息主题(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,在了解上面概念后我们直接上代码看下重写监听的方式吧

重写监听

  1. 开启redis key变化事件
    redis配置文件配置 notify-keyspace-events AKEx ,默认是关闭的
  2. 引入依赖(其他正常的依赖省略了,记得添加哈)
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
  1. 写个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;
    }
}
  1. 写个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 这个是更新的topic
  • 写个RedisKeyUpdateListener extent KeyUpdateEventMessageListener,重写onMessage方法

ok,写完了,插个tips。Python开发的文件类型转化windows工具,支持png,jpeg,ico等文件类型互转

我们继续看下容器注册方式

容器注册

上面的配置就不重写了

  1. 注意我们写个 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));
    }
}
  1. 然后修改下容器配置
@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过期了就是监听不到呢

  • 确保项目能启动
  • 确保依赖是否冲突了
  • 确保redis配置开启了 notify-keyspace-events AKEx

为啥我配置了notify-keyspace-events 。就是没有监听到key 更新事件呢?

  • 确保配置的是 notify-keyspace-events AKEx,而不是 notify-keyspace-events Ex。ex只能监听到过期事件,而监听不到删除事件

鸣谢

  • 非常感谢你从头到尾阅读了这篇文章,希望其中的内容对你有所启发和帮助。如果你还有其他问题或需要进一步的了解,欢迎随时关注我的动态并留言
  • 最后希望大家给作者点个关注和小赞赞支持下,创作不易啊
  • 觉得有收藏价值也可以进行收藏
  • 最后给大家来波小tips。优雅封装接口给第三方调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94317.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

centos7删除乱码文件

centos7删除乱码文件1. 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.解释 当文件名为乱码的时候&#xff0c;无法通过键盘输入文件名&#xff0c;所以在终端下就不能直接利用rm&#xff0c;mv等命令管理文件了。 但是每个文件都有一个i节点号&#xff0c;可以通过…

Docker:Harbor 私有仓库迁移

Harbor 私有仓库迁移 一.私有仓库迁移的介绍 1.为何要对Harbor 私有仓库的迁移 &#xff08;1&#xff09;硬件升级或更换&#xff1a;如果源 Harbor 在旧的硬件设备上运行&#xff0c;并且计划将其迁移到新的硬件设备上&#xff0c;那么需要执行迁移操作。 &#xff08;2&…

macOS上开源免费的新闻阅读器SABnzbd

SABnzbd Mac版是一款运行在Mac平台上的开源新闻阅读器&#xff0c;这款阅读器界面简约、功效简单强大&#xff0c;使用SABnzbd时可以帮助使用Python语言编写&#xff0c;让用户使用usenet新闻组更便利&#xff0c;是你阅读新闻的好帮手&#xff01; SABnzbd具有以下主要特点&a…

【Python编程】将同一种图片分类到同一文件夹中

一、数据结构如下&#xff1a; 二、编程工具&#xff1a;Jupyter-Notebook 三、代码&#xff1a; import os import cv2 import shutilpath0os.getcwd()\\apple\\RGB path1os.getcwd()\\apple\\tof_confidence path2os.getcwd()\\apple\\tof_depth path3os.getcwd()\\apple\\…

【洛谷算法题】P1001-A+B Problem【入门1顺序结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P1001-AB Problem【入门1顺序结构】&#x1f30f;题目背景&#x1f30f;题目描述…

统计学补充概念-13-逻辑回归

概念 逻辑回归&#xff08;Logistic Regression&#xff09;实际上是一种用于解决分类问题的统计学习方法&#xff0c;尽管其名称中带有"回归"一词&#xff0c;但它主要用于处理分类任务。逻辑回归用于预测一个事件发生的概率&#xff0c;并将其映射到一个特定的输出…

使用通信顺序进程(CSP)模型的 Go 语言通道

在并发编程中&#xff0c;许多编程语言采用共享内存/状态模型。然而&#xff0c;Go 通过实现 通信顺序进程&#xff08;CSP&#xff09;模型来区别于众多。在CSP中&#xff0c;程序由不共享状态的并行进程组成&#xff1b;相反&#xff0c;它们通过通道进行通信和同步操作。因此…

长胜证券:沪指探底回升涨0.47%,券商、酿酒板块拉升,传媒板块活跃

24日早盘&#xff0c;沪指盘中震动回落&#xff0c;接近午盘快速拉升走高&#xff1b;深成指、创业板指强势上扬&#xff1b;北向资金今天转向&#xff0c;早盘积极出场&#xff0c;半日净买入近30亿元。 到午间收盘&#xff0c;沪指涨0.47%报3092.88点&#xff0c;深成指涨1.1…

面试现场表现:展示你的编程能力和沟通技巧

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

算法通关村第5关【白银】| 哈希和栈经典算法题

1.两个栈实现队列 思路&#xff1a;两个栈&#xff0c;一个输入栈&#xff0c;一个输出栈。 当需要输入的时候就往inStack中插入&#xff0c;需要输出就往outStack中输出&#xff0c;当输出栈是空就倒出输入栈的数据到输出栈中&#xff0c;这样就保证了后插入的数据从栈顶倒入…

基于面向对象的空间自相关指数,即插即用!Moran‘s I,局部莫兰指数,Geary‘s C指数,附完整可行使用案例

Geary’s C Geary’s C&#xff08;也称为Geary’s coefficient&#xff09;是一种用于衡量空间自相关性的统计指标&#xff0c;它可以用来评估地理数据中的空间聚集或离散程度。Geary’s指数的计算公式如下&#xff1a; G ( n − 1 ) ∗ ( Σ Σ w i j ∗ ( x i − x j ) 2…

云计算企业私有云平台建设方案PPT

导读&#xff1a;原文《云计算企业私有云平台建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发本文&#xff0c;…

华为云Stack的学习(二)

三、华为云Stack产品组件 FunsionSphere CPS 提供云平台的基础管理和业务资源&#xff08;包括计算资源和存储资源&#xff09;。采用物理服务器方式部署在管理节点。可以做集群的配置&#xff0c;扩容和运维管理。 Service OM 提供云服务的运维能力&#xff0c;采用虚拟化方…

PMAC与Modbus主站进行Modbus Tcp通讯

PMAC与Modbus主站进行Modbus Tcp通讯 创建modbus通讯参数 在项目的PMAC Script Language\Global Includes下创建一个名为00_Modbus_Para.pmh的pmh文件。 Modbus[0].Config.ServerPort 0 Modbus[0].Config.ConnectTimeOut 6000 Modbus[0].Config.SendRecvTimeOut 0 Modbu…

阿里云通义千问开源第二波!大规模视觉语言模型Qwen-VL上线魔搭社区

通义千问开源第二波&#xff01;8月25日消息&#xff0c;阿里云推出大规模视觉语言模型Qwen-VL&#xff0c;一步到位、直接开源。Qwen-VL以通义千问70亿参数模型Qwen-7B为基座语言模型研发&#xff0c;支持图文输入&#xff0c;具备多模态信息理解能力。在主流的多模态任务评测…

第七周第七天学习总结 | MySQL入门及练习学习第二天

实操练习&#xff1a; 1.创建一个名为 cesh的数据库 2.在这个数据库内 创建一个名为 xinxi 的表要求该表可以包含&#xff1a;编号&#xff0c;姓名&#xff0c;备注的信息 3.为 ceshi 表 添加数据 4.为xinxi 表的数据设置中文别名 5.查询 在 xinxi 表中编号 为2 的全部…

UNIX网络编程卷一 学习笔记 第二十八章 原始套接字

原始套接字提供普通的TCP和UDP套接字不具备的以下3个能力&#xff1a; 1.有了原始套接字&#xff0c;进程可以读写ICMPv4、IGMPv4、ICMPv6等分组。例如&#xff0c;ping程序就使用原始套接字发送ICMP回射请求并接收ICMP回射应答。多播路由守护程序mrouted也使用原始套接字发送和…

Apache Celeborn 让 Spark 和 Flink 更快更稳更弹性

摘要&#xff1a;本文整理自阿里云/数据湖 Spark 引擎负责人周克勇&#xff08;一锤&#xff09;在 Streaming Lakehouse Meetup 的分享。内容主要分为五个部分&#xff1a; Apache Celeborn 的背景Apache Celeborn——快Apache Celeborn——稳Apache Celeborn——弹Evaluation…

MySQL binlog的几种日志录入格式以及区别

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

【java安全】JNDI注入概述

文章目录 【java安全】JNDI注入概述什么是JNDI&#xff1f;JDNI的结构InitialContext - 上下文Reference - 引用 JNDI注入JNDI & RMI利用版本&#xff1a;JNDI注入使用Reference 【java安全】JNDI注入概述 什么是JNDI&#xff1f; JNDI(Java Naming and Directory Interf…