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.hqwc.cn/news/256566.html

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

相关文章

javaee实验:Spring Boot 整合 Mybatis

目录 MybatisMyBatis 框架简介Mybatis 框架执行流程图映射器 实验目的实验内容实验过程数据库准备项目结构代码实现 实验结果 Mybatis MyBatis 框架简介 Mybatis 的前身是 Apache 的开源框架 iBatis&#xff0c;与 Hibernate 一样是一个 Java 持久层的框 架。Mybatis 的优势在…

SpringBoot框架+原生HTML开发,基于云端SaaS服务方式的电子病历编辑器源码

一体化电子病历编辑器源码&#xff0c;电子病历系统 一体化电子病历系统基于云端SaaS服务的方式&#xff0c;采用B/S&#xff08;Browser/Server&#xff09;架构提供&#xff0c;覆盖了医疗机构电子病历模板制作到管理使用的整个流程。除实现在线制作内容丰富、图文并茂、功能…

题目:回文判定(蓝桥OJ 1371)

题目描述&#xff1a; 解题思路&#xff1a; 可以采用双指针判断&#xff08;这里说的指针其实是用下标表示&#xff09;。 题解&#xff1a; #include<bits/stdc.h> using namespace std;const int N 1e6 9;//注意大小 char s[N];//在全局写&#xff0c;默认内部为空…

常见动物经济手术3d模拟交互演示教学实现了教育资源的共享

动物常见病防治是兽医必备的技能&#xff0c;为了让实习兽医在上岗作业前拥有丰富的常见病防治经验。借助动物常见病防治VR虚拟仿真技术开展动物常见病防治VR模拟实操培训&#xff0c;能极大方便院校实训。 提高教学质量 传统的动物医学教学往往依赖于理论知识和实验室实践&…

RHEL8_Linux硬盘管理

主要介绍Linux磁盘管理 了解分区的概念对硬盘进行分区常见的分区swap分区的管理 1.了解分区的概念 1&#xff09;新的硬盘首先需要对其进行分区和格式化&#xff0c;下面来了解以下硬盘的结构&#xff0c;如图。 2&#xff09;硬盘的磁盘上有一个个圈&#xff0c;每两个圈组…

Axure原型图表组件库,数据可视化元件(Axure9大屏组件)

针对Axure制作的大屏图表元件库&#xff0c;帮助产品经理更高效地制作高保真图表原型&#xff0c;是产品经理必备元件工具。现分享完整的组件库&#xff0c;大家一起学习。 本组件库的图表模块&#xff0c;已包含所有常用的图表&#xff0c;以下为部分组件截图示意。文末可下载…

Halcon reduce_domain和scale_image的作用

在Halcon中&#xff0c;reduce_domain是用于缩小图像域&#xff08;Image Domain&#xff09;的操作。 它的作用是通过指定一个感兴趣区域&#xff08;ROI&#xff0c;Region of Interest&#xff09;&#xff0c;将图像数据限制在该区域内&#xff0c;从而实现对图像进行裁剪…

uni-app 微信小程序之好看的ui登录页面(一)

文章目录 1. 页面效果2. 页面样式代码 更多登录ui页面 uni-app 微信小程序之好看的ui登录页面&#xff08;一&#xff09; uni-app 微信小程序之好看的ui登录页面&#xff08;二&#xff09; uni-app 微信小程序之好看的ui登录页面&#xff08;三&#xff09; uni-app 微信小程…

如何优雅使用 vue-html2pdf 插件生成pdf报表

使用 vue-html2pdf 插件 业务背景&#xff0c;老板想要一份能征服客户的pdf报表&#xff0c;传统的pdf要手撕&#xff0c;企业中确实有点耗费时间&#xff0c;于是github上面看到开源的这个插件就…废话不多说&#xff0c;直接上教程 1.使用下面命令安装 vue-html2pdf npm i…

【dig命令查询方法】

dig&#xff08;Domain Information Groper&#xff09;是一个用于查询DNS&#xff08;域名系统&#xff09;的命令行工具&#xff0c;它可以帮助您获取关于域名的各种信息&#xff0c;如IP地址、MX记录、NS记录等。下面是dig的详细使用教程。 基本语法&#xff1a; dig [ser…

苹果mac电脑如何彻底删除卸载软件?

在苹果电脑上安装和使用软件非常容易&#xff0c;但是卸载软件却可能会变得复杂和困难。不像在Windows上&#xff0c;你不能简单地在控制面板中找到已安装的程序并卸载它们。因此&#xff0c;在这篇文章中&#xff0c;我们将讨论苹果电脑怎么彻底删除软件。 CleanMyMac X全新版…

进行生成简单数字图片

1.之前只能做一些图像预测,我有个大胆的想法,如果神经网络正向就是预测图片的类别,如果我只有一个类别那就可以进行生成图片,专业术语叫做gan对抗网络 2.训练代码 import torch import torch.nn as nn import torch.optim as optim import torchvision.transforms as transfo…