Spring boot 使用Redis 消息发布订阅

Spring boot 使用Redis 消息发布订阅

文章目录

  • Spring boot 使用Redis 消息发布订阅
    • Redis 消息发布订阅
        • Redis 发布订阅 命令
    • Spring boot 实现消息发布订阅
      • 发布消息
      • 消息监听
      • 主题订阅
    • Spring boot 监听 Key 过期事件
      • 消息监听
      • 主题订阅

最近在做请求风控的时候,在网上搜集了大量的解决方案,最后使用Redis 消息发布订阅 比较符合业务。做一下记录

img

Redis 消息发布订阅

img

Redis 发布订阅 命令:redis命令手册

1、Redis 中"pub/sub"的消息,为"即发即失",server 不会保存消息,如果 publish 的消息没有任何 client 处于 “subscribe” 状态,消息将会被丢弃;如果 client 在 subcribe 时,链接断开后重连,那在么此期间的消息也将丢失。

2、Redis server 将会"尽力"将消息发送给处于 subscribe 状态的 client,但是仍不会保证每条消息都能被正确接收。

**优点:**支持发布订阅,支持多组生产者、消费者处理消息

缺点:

  1. 消费者下线数据会丢失

  2. 不支持数据持久化,Redis宕机则数据也会丢失

  3. 消息堆积,缓存区溢出,消费者会被强制踢下线,数据也会丢失

Redis 发布订阅 命令
命令描述
Redis Unsubscribe 命令指退订给定的频道。
Redis Subscribe 命令订阅给定的一个或多个频道的信息。
Redis Pubsub 命令查看订阅与发布系统状态。
Redis Punsubscribe 命令退订所有给定模式的频道。
Redis Publish 命令将信息发送到指定的频道。
Redis Psubscribe 命令订阅一个或多个符合给定模式的频道。

Spring boot 实现消息发布订阅

1、引入 Redis 依赖

    <!--Spring Boot redis 启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

2、Redis 数据库配置

spring:data:redis:database: 0host: localhostport: 6379password:

发布消息

	/*** redis 将信息发送到指定的频道* @param topic   :消息所属的主题/频道* @param context :消息内容* @return*/redisTemplate.convertAndSend(topic, context);
@RequiredArgsConstructor
@Service
public class RequestRateLimiterService {private final RedisTemplate<String, Object> redisTemplate;// Redis 中的 key 前缀private static final String REDIS_KEY_PREFIX = "select_rate_limit:";// Redis 中的通道名称private static final String REDIS_CHANNEL = "select_rate_limit_channel";// 根据用户名 请求风控public boolean allowRequest(String username) {// 每分钟最大请求次数Long MAX_REQUESTS_PER_MINUTE = 60L;String key = REDIS_KEY_PREFIX + username;Long currentRequests = redisTemplate.opsForValue().increment(key);if (currentRequests != null && currentRequests > MAX_REQUESTS_PER_MINUTE) {redisTemplate.convertAndSend(REDIS_CHANNEL, username);return false; // 超过阈值,拒绝请求}if (currentRequests != null && currentRequests == 1) {redisTemplate.expire(key, 1, TimeUnit.MINUTES); // 设置过期时间为1分钟}return true; // 允许请求}}

消息监听

1、 Redis 消息订阅-消息监听器,当收到阅订的消息时,会将消息交给这个类处理。

/*** Redis 消息订阅-消息监听器,当收到阅订的消息时,会将消息交给这个类处理* <p>* 1、可以直接实现 MessageListener 接口,也可以继承它的实现类 MessageListenerAdapter.* 2、自动多线程处理,打印日志即可看出,即使手动延迟,也不会影响后面消息的接收。**/
@Component
public class RequestRateLimitSubscriber implements MessageListener {// 直接从容器中获取@Resourceprivate RedisTemplate<String, Object> redisTemplate;/*** 监听到的消息必须进行与发送时相同的方式进行反序列* 1、订阅端与发布端 Redis 序列化的方式必须相同,否则会乱码。** @param message :消息实体* @param pattern :匹配模式*/@Overridepublic void onMessage(Message message, byte[] pattern) {// 消息订阅的匹配规则,如 new PatternTopic("basic-*") 中的 basic-*String msgPattern = new String(pattern);// 消息所属的通道,可以根据不同的通道做不同的业务逻辑String channel = (String) redisTemplate.getStringSerializer().deserialize(message.getChannel());// 接收的消息内容,可以根据自己需要强转为自己需要的对象,但最好先使用 instanceof 判断一下Object body = redisTemplate.getValueSerializer().deserialize(message.getBody());log.info("收到 Redis 订阅消息: channel={} body={} pattern={} ", channel, body, msgPattern);// 模拟数据处理 ********// 发送警告通知,可以通过邮件、短信等方式进行通知log.info("------------数据处理完成.......");}
}

主题订阅

1、自定义 RedisTemplate 序列化方式(发布者和订阅者必须相同)。

2、配置主题订阅 - Redis 消息监听器绑定监听指定通道。

/*** 自定义 RedisTemplate 序列化方式* 配置主题订阅 - Redis 消息监听器绑定监听指定通道*/
@Configuration
public class RedisConfig {// 自定义的消息订阅监听器,当收到阅订的消息时,会将消息交给这个类处理@Resourceprivate RequestRateLimitSubscriber requestRateLimitSubscriber;//  自定义 RedisTemplate 序列化方式   @Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(RedisSerializer.string());// key 序列化规则redisTemplate.setHashKeySerializer(RedisSerializer.string());// hash key 序列化规则redisTemplate.setValueSerializer(RedisSerializer.java());// value 序列化规则redisTemplate.setHashValueSerializer(RedisSerializer.java()); // hash value 序列化规则redisTemplate.setConnectionFactory(factory); //绑定 RedisConnectionFactoryreturn redisTemplate; //返回设置好的 RedisTemplate}/*** 配置主题订阅* RedisMessageListenerContainer - Redis 消息监听器绑定监听指定通道* 1、可以添加多个监听器,监听多个通道,只需要将消息监听器与订阅的通道/主题绑定即可。* 2、订阅的通道可以配置在全局配置文件中,也可以配置在数据库中,* <p>* addMessageListener(MessageListener listener, Collection<? extends Topic> topics):将消息监听器与多个订阅的通道/主题绑定* addMessageListener(MessageListener listener, Topic topic):将消息监听器与订阅的通道/主题绑定** @param connectionFactory* @return*/@Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();// 设置连接工厂,RedisConnectionFactory 可以直接从容器中取,也可以从 RedisTemplate 中取container.setConnectionFactory(factory);// 订阅名称叫 select_rate_limit_channel 的通道, 类似 Redis 中的 subscribe 命令container.addMessageListener(requestRateLimitSubscriber, new ChannelTopic("*"));// 订阅名称以 'basic-' 开头的全部通道, 类似 Redis 的 pSubscribe 命令container.addMessageListener(requestRateLimitSubscriber, new PatternTopic("*"));return container;}
}

Spring boot 监听 Key 过期事件

1、Redis 数据库可以通过命令设置 Key 的有效时间,当一个 Key 过期后会自动从数据库中删除,释放空间。得益于于这个特性,可以很轻松地实现诸多类似于 “Session” 管理、数据缓存等功能。它们都有一个共同点就是,数据不会永久保存!

2、在有些场景中,可能希望在某些 Key 过期的时候获取到通知,进行一些业务处理。或者是干脆用于 “定时通知/任务” 功能,例如:下单 30 分钟后未支付,则取消订单。那么可以在用户下单的时候使用订单号作为 key 设置到 Redis 数据库中,并且设置过期时间为 30 分钟。当超时后,可以在 “key 过期通知” 中获取到 key 也就是订单号,判断用户是否已经支付从而是否取消订单。

3、Redis 的 Key 过期通知功能本质上是通过 发布/订阅 功能实现的,所以它「不能保证通知消息的交付」,当 Key 过期时如果服务器停机、重启后则该通知消息会永久丢失。

消息监听

1、Spring Data Redis 专门提供了一个密钥过期事件消息侦听器:KeyExpirationEventMessageListener,自定义监听器类继承它,然后覆写 doHandleMessage(Message message) 方法即可。

2、doHandleMessage 方法用于处理 Redis Key 过期通知事件,其中 Message 参数表示通知消息,只有 2 属性,分别表示消息正文(在这里就是过期的 Key 名称)以及来自于哪个 channel。

3、在 Redis Key 过期事件中,「只能获取到已过期的 Key 的名称,不能获取到值。」

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
/*** Redis 缓存 Key 过期监听器* Spring Data Redis 专门提供了一个密钥过期事件消息侦听器:KeyExpirationEventMessageListener,* 自定义监听器类继承它,然后覆写 doHandleMessage(Message message) 方法即可。*/
@Component
public class KeyExpireListener extends KeyExpirationEventMessageListener {private static final Logger logger = LoggerFactory.getLogger(KeyExpireListener.class);/*** 通过构造函数注入 RedisMessageListenerContainer 给 KeyExpirationEventMessageListener** @param listenerContainer : Redis消息侦听器容器*/public KeyExpireListener(RedisMessageListenerContainer listenerContainer) {super(listenerContainer);}/*** doHandleMessage 方法用于处理 Redis Key 过期通知事件,* 在 Redis Key 过期事件中,「只能获取到已过期的 Key 的名称,不能获取到值。」** @param message:通知消息,只有 2 属性,分别表示消息正文(在这里就是过期的 Key 名称)以及来自于哪个 channel。*/@Overridepublic void doHandleMessage(Message message) {// 过期的 keyString key = new String(message.getBody());// 消息通道String channel = new String(message.getChannel());logger.info("过期key={} 消息通道(channel)={}", key, channel);}
}

主题订阅

1、与上面稍微有点不同,因为 key 过期事件属于 Redis 内部消息,内部频道/通道,所以只需要往容器中注入 RedisMessageListenerContainer 就行,不需要 addMessageListener 手动设置监听器 监听指定的通道/频道(topic 表达式)。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisConfig {@Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(factory);//  container.setTaskExecutor(null);            // 设置用于执行监听器方法的 Executor//  container.setErrorHandler(null);            // 设置监听器方法执行过程中出现异常的处理器//  container.addMessageListener(null, null);   // 手动设置监听器 & 监听的 topic 表达式return container;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全套的外贸出口业务流程,赶紧收藏起来吧

很多做外贸的小伙伴入行遇到的第一个问题就是对外贸业务流程的不熟悉&#xff0c;今天小易给大家整理了一份外贸业务全流程&#xff0c;从开发客户到售后服务一整套流程&#xff0c;一起来看看吧&#xff01; 目前做外贸开发客户的渠道一般有以下几种&#xff1a; 1、自建站、外…

如何在 Windows 中恢复已删除的 Excel 文件?– 8 个有效方法!

如何恢复已删除的Excel文件&#xff1f;如果您不小心删除了 Excel 文件或该文件已损坏&#xff0c;您无需担心会丢失宝贵的数据。MiniTool 分区向导的这篇文章提供了 8 种有效的方法来帮助您恢复它们。 Microsoft Excel 是 Microsoft 为 Windows、macOS、Android、iOS 和 iPad…

【lesson4】数据类型之数值类型

文章目录 数据分类数值类型tinyint类型有符号类型测试无符号类型测试 bit类型测试 float类型有符号测试无符号测试 decimal类型测试 数据分类 数值类型 tinyint类型 说明&#xff1a;tinyint 有符号能存储的范围是-128-127&#xff0c;无符号能存储的范围是0~255 有符号类型…

蓝桥杯-动态规划专题-子数组系列,双指针

目录 一、单词拆分 二、环绕字符串中唯一的子字符串 双指针-三数之和 ArrayList(Arrays.asList(array)) 四、四数之和&#xff08;思路和三数之和一样&#xff0c;只是多了一层循环&#xff09; 一、单词拆分 1.状态表示 dp[i]:到达i位置结尾&#xff0c;能否被dict拆分 …

Terraform实战(二)-terraform创建阿里云资源

1 初始化环境 1.1 创建初始文件夹 $ cd /data $ mkdir terraform $ mkdir aliyun terraform作为terraform的配置文件夹&#xff0c;内部的每一个.tf&#xff0c;.tfvars文件都会被加载。 1.2 配置provider 创建providers.tf文件&#xff0c;配置provider依赖。 provider…

想学编程,但不知道从哪里学起,应该怎么办?

怎样学习任何一种编程语言 我将教你怎样学习任何一种你将来可能要学习的编程语言。本书的章节是基于我和很多程序员学习编程的经历组织的&#xff0c;下面是我通常遵循的流程。 1&#xff0e;找到关于这种编程语言的书或介绍性读物。 2&#xff0e;通读这本书&#xff0c;把…

基于Java Swing泡泡龙游戏(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

AP9111手电筒专用集成电路芯片 单节干电池 LED手电筒IC

概述 AP9111 是 LED 手电筒专用集成电路芯片 &#xff0c;是一款采用大规模集成电路技术&#xff0c;专门针对单节干电池的 LED 手电筒设计的一款专用集成电路。外加 1 个电感元件&#xff0c;即可构成 LED 手电筒驱动电路板。AP 9111 性能优越、可靠性高、使用简单、生产一致…

六级高频词汇3

目录 单词 参考链接 单词 400. nonsense n. 胡说&#xff0c;冒失的行动 401. nuclear a. 核子的&#xff0c;核能的 402. nucleus n. 核 403. retail n. /v. /ad. 零售 404. retain vt. 保留&#xff0c;保持 405. restrict vt. 限制&#xff0c;约束 406. sponsor n. …

聊个开心的敏捷话题——40小时工作制

近年来&#xff0c;加班现象在很多行业已经普遍制度化&#xff0c;甚至“996”已成为一些行业标签。企业高强度的压榨让员工不堪重负&#xff0c;且时常由此引发的各种悲剧也并不鲜见。 所以&#xff0c;今天我们一起来聊一个开心轻松的话题——极限编程的40h工作制原则。 40…

【环境搭建】ubuntu22安装ros2

基于某种特殊需求&#xff0c;从Ubuntu16到22目前都尝试过安装ros、ros2 参考1&#xff1a;http://t.csdnimg.cn/DzvSe 参考2&#xff1a;http://t.csdnimg.cn/sOzr1 1.设置locale sudo apt update && sudo apt install locales sudo locale-gen en_US en_US.UTF-8 s…

Spring的IOC容器初始化流程

Spring的IOC容器初始化流程 IOC容器初始化在SpringApplication对象创建完毕执行run方法时执行refreshContext()时开始。 准备BeanFactory&#xff0c;设置其类加载器和environment等 执行BeanFactory后置处理器&#xff0c;扫描要放入容器的Bean信息&#xff0c;得到对应的Bea…

阿里云服务器租用价格分享,阿里云服务器热门配置最新活动价格汇总

在我们购买阿里云服务器的时候&#xff0c;1核2G、2核2G、2核4G、2核8G、4核8G、8核16G、8核32G等配置属于用户购买最多的热门配置&#xff0c;1核2G、2核2G、2核4G这些配置低一点的云服务器基本上能够满足绝大部分个人建站和普通企业用户建站需求&#xff0c;而4核8G、8核16G、…

Maven项目引入本地jar

Maven项目引入本地jar 1.对应maven模块项目中建lib目录&#xff0c;将jar放入进去 2.在对应的模块pom.xml中引入此依赖jar 3.在对应的maven-plugin插件打包的pom.xml中指定需要includeSystemScope为true的jar

AMEYA360:大唐恩智浦荣获 2023芯向亦庄 “汽车芯片50强”

2023年11月28日&#xff0c;由北京市科学技术委员会和北京市经济和信息化局指导、北京经济技术开发区管理委员会主办、盖世汽车协办的“芯向亦庄”汽车芯片大赛在北京亦庄成功闭幕。 在本次大赛中 大唐恩智浦的 电池管理芯片DNB1168 (应用于新能源汽车BMS系统) 凭卓越的性能及高…

【SpringBoot教程】SpringBoot 实现前后端分离的跨域访问(CORS)

作者简介&#xff1a;大家好&#xff0c;我是撸代码的羊驼&#xff0c;前阿里巴巴架构师&#xff0c;现某互联网公司CTO 联系v&#xff1a;sulny_ann&#xff08;17362204968&#xff09;&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗…

【毕业季|进击的技术er】作为一名职场人,精心总结的嵌入式学习路线图

活动地址&#xff1a;毕业季进击的技术er 文章目录 0、作者介绍1、前言2、嵌入式基础必备知识2.1、学习内容2.2、学习建议2.3、学习资料 3、嵌入式入门篇——51单片机3.1、学习内容3.2、学习建议3.3、学习资料 4、STM32进阶篇4.1、学习内容4.2、学习建议4.3、学习资料 5、小而美…

嵌入式开发按怎样的路线学习较好?

嵌入式开发按怎样的路线学习较好&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「嵌入式从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&…

张驰咨询:掌握流程改进的关键,深入了解六西格玛绿带培训

尊敬的读者&#xff0c;当您寻求提升个人能力&#xff0c;加强企业流程管理时&#xff0c;六西格玛绿带培训无疑是您的不二选择。本文将带您深入了解六西格玛绿带培训的核心内容、必备工具和实际案例&#xff0c;以助您在职业生涯中一帆风顺。 六西格玛绿带培训主要针对中层管…

论文查重怎么找到需要更改的【详细说明】

大家好&#xff0c;今天来聊聊论文查重怎么找到需要更改的&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 论文查重怎么找到需要更改的 论文查重是保证学术诚信和提高论文质量的重要环节小发猫伪原创。…