黑马点评--Redis消息队列

news/2024/5/2 10:15:32/文章来源:https://blog.csdn.net/weixin_53050118/article/details/128029145

Redis消息队列

Redis消息队列实现异步秒杀

消息队列(Message Queue),字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色:

  • 消息队列:存储和管理消息,也被称为消息代理(Message Broker)
  • 生产者:发送消息到消息队列
  • 消费者:从消息队列获取消息并处理消息

Redis提供了三种不同的方式来实现消息队列

  • list结构:基于List结构模拟消息队列
  • PubSub:基本的点对点消息模型
  • Stream:比较完善的消息队列模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w7dtXsVa-1669305234332)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221123211303290.png)]

基于List结构模拟消息队列

消息队列(Message Queue),字面意思就是存放消息的队列。而Redis的list数据结构是一个双向链表,很容易模拟出队列效果。

队列是入口和出口不在一边,因此我们可以利用:LPUSH结合RPOP,或者RPUSH结合LPOP来实现。

不过要注意的是,当队列中没有消息时RPOP或LPOP操作会返回null,并不像JVM的阻塞队列那样会阻塞并等待消息。因此这里应该使用BRPOP或者BLPOP来实现阻塞效果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFz1E6uV-1669305234335)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221123211941003.png)]

基于List的消息队列有哪些优缺点?

优点:

  • 利用Redis存储,不受限于JVM内存上限
  • 基于Redis的持久化机制,数据安全性有保证
  • 可以满足消息有序性

缺点:

  • 无法避免消息丢失
  • 只支持单消费者

基于PubSub的消息队列

PubSub(发布订阅)是Redis2.0版本引入的消息传递模型。顾名思义,消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。

  • SUBSCRIBE channel [channel]:订阅一个或多个频道
  • PUBLISH channel msg:向一个频道发送消息
  • PSUBSCRIBE pattern[pattern] :订阅与pattern格式匹配的所有频道

优点:

采用发布订阅模型,支持多生产,多消费

缺点:

  • 不支持数据持久化
  • 无法避免消息丢失
  • 消息堆积有上限,超出时数据丢失

基于Stream的消息队列

Stream是Redis5.0引入的一种新数据结构,可以实现一个功能完善的消息队列。发送消息的命令:XADD

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g5GCJ2qf-1669305234337)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124144846936.png)]

读取消息的方式之一:XREAD

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-In5pQWmB-1669305234338)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124145521507.png)]

XREAD阻塞方式,读取最新的消息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqOgMaT7-1669305234340)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124145647904.png)]

在业务开发中,我们可以循环的调用XREAD阻塞方式来查询最新消息,从而实现持续监听队列的效果,伪代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DrURHJMO-1669305234341)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124150129390.png)]

STREAM类型消息队列的XREAD命令特点:

  • 消息可回溯
  • 一个消息可以被多个消费者读取
  • 可以阻塞读取
  • 有消息漏读的风险

基于Stream的消息队列-消费者组

消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。具备下列特点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FGTmKECR-1669305234343)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124151543118.png)]

创建消费者组:

XGROUP CREATE key groupName ID [MKSTREAM]
  • key:队列名称
  • groupName:消费者组名称
  • ID:起始ID标示,$代表队中最后一个消息,0则代表队列中第一个消息
  • MKSTREAM:队列不存在时自动创建队列

其它常见命令:

#删除指定的消费者组
XGROUP DESTORY Key groupName#给指定的消费者组添加消费者
XGROUP CREATECONSUMER key groupname consumername#删除消费者组中的指定消费者
XGROUP DELCONSUMER key groupname consumername

从消费者组读取消息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TrEW9AGt-1669305234352)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124152437650.png)]

  • group:消费组名称
  • consumer:消息者名称,如果消费者不存在,会自动创建一个消费者
  • count:本次查询的最大数量
  • BLOCK milliseconds:当没有消息时最长等待时间
  • NOACK:无需手动ACK,获取到消息后自动确定
  • STREAMS key:指定队列名称
  • ID:获取消息的起始ID:
    • “>”:从下一个未消费的消息开始
    • 其它:根据指定id从pending-list中获取已消费但未确认的消息,例如0,是从pending-list中的第一个消息开始

消费者监听消息的基本思路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwxAgE35-1669305234354)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124154427618.png)]

Stream类型消息队列的XREADGROUP命令特点:

  • 消息可回溯
  • 可以多消费争抢消息,加快消费速度
  • 可以阻塞读取
  • 没有消息漏读的风险
  • 有消息确认机制,保证消息至少被消费一次

Redis消息队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BB8Z2Zgi-1669305234356)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221124154752296.png)]

基于Redis的Stream结构作为消息队列,实现异步秒杀下单

**注意:**redis 要求 5.0+因为使用到了 stream 特性。

需求:

  1. 创建一个Stream类型的消息队列,名为stream.orders

    XGROUP CREATE stream.orders g1 0 MKSTREAM
    
  2. 修改之前的秒杀下单Lua脚本,在认定有抢购资格后,直接向stream.orders中添加消息,内容包含voucherId,userId,orderId

    改变的lua脚本:

    --1.3.订单id
    local orderId = ARGV[3]--3.6.发送消息到队列中,XADD stream.orders * k1 v1 k2 v2 ...
    redis.call('xadd','stream.orders','*','userId',userId,'voucherId',voucherId,'id',orderId)
    
  3. 项目启动时,开启一个线程任务,尝试获取stream.orders中的消息,完成下单

//线程任务private  class  VoucherOrderHandler implements Runnable{String queueName = "streams.orders";@Overridepublic void run() {while (true){try {//1.获取消息队列中的订单信息 XREADGROUP GROUP g1 c1 COUNT1 BLOCK 2000 STREAMS streams.orderList<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),StreamOffset.create(queueName, ReadOffset.lastConsumed()));//2.判断消息获取是否成功if (list ==null || list.isEmpty()) {//2.1如果获取失败,说明没有消息,继续下一次循环continue;}//3.解析消息中的订单消息MapRecord<String, Object, Object> record = list.get(0);Map<Object, Object> value = record.getValue();VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);//4.如果获取成功,可以下单handleVoucherOrder(voucherOrder);//5.ACK确认   SACK stream.orders g1 idstringRedisTemplate.opsForStream().acknowledge(queueName,"g1",record.getId());} catch (Exception e) {log.error("处理订单异常",e);//处理异常的消息handlePendingList();}}}private void handlePendingList(){while (true){try {//1.获取pending-list中的订单信息 XREADGROUP GROUP g1 c1 COUNT1 BLOCK 2000 STREAMS streams.orderList<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1),StreamOffset.create(queueName, ReadOffset.from("0")));//2.判断消息获取是否成功if (list ==null || list.isEmpty()) {//2.1如果获取失败,说明pending-list没有消息,结束循环break;}//3.解析消息中的订单消息MapRecord<String, Object, Object> record = list.get(0);Map<Object, Object> value = record.getValue();VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);//4.如果获取成功,可以下单handleVoucherOrder(voucherOrder);//5.ACK确认   SACK stream.orders g1 idstringRedisTemplate.opsForStream().acknowledge(queueName,"g1",record.getId());} catch (Exception e) {log.error("处理pending-list订单异常",e);try {Thread.sleep(20);} catch (InterruptedException ex) {ex.printStackTrace();}}}}}

实现下单业务

 @Override@Transactionalpublic Result seckillVoucher(Long voucherId) {//获取用户Long userId = UserHolder.getUser().getId();//获取订单idlong orderId = redisIdWorker.nextId("order");//1.执行lua脚本Long result = stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(),userId.toString(),String.valueOf(orderId));//2.判断结果是为0int r = result.intValue();if (r !=0 ){//2.1.不为0,代表没有购买资格return  Result.fail(r==1 ? "库存不足":"不能重复下单");}//3.获取代理对象proxy = (IVoucherOrderService) AopContext.currentProxy();//3.返回订单idreturn Result.ok(orderId);}

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

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

相关文章

【附源码】计算机毕业设计JAVA疫情下的居民管理系统

【附源码】计算机毕业设计JAVA疫情下的居民管理系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA…

蒙泰转债上市价格预测

蒙泰转债基本信息转债名称&#xff1a;蒙泰转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;3.0亿元。正股名称&#xff1a;蒙泰高新&#xff0c;今日收盘价&#xff1a;31.3&#xff0c;转股价格&#xff1a;26.15。当前转股价值 转债面值 / 转股价格 * 正…

有没有把语音转为文字的软件?这几个转换软件你值得收藏

我们在日常的工作和生活中&#xff0c;应该经常会遇到需要将音频转换成文字的情况吧。相信大部分的小伙伴都会选择直接使用转换软件进行音频转文字的操作&#xff0c;但在使用的过程中就会发现&#xff0c;有些软件会在使用次数、音频时长上面有所限制&#xff0c;导致我们会转…

《从零开始:机器学习的数学原理和算法实践》chap1

《从零开始&#xff1a;机器学习的数学原理和算法实践》chap1 学习笔记 文章目录《从零开始&#xff1a;机器学习的数学原理和算法实践》chap1 学习笔记chap1 补基础&#xff1a;不怕学不懂微积分1.1 深入理解导数的本质直观理解复合函数求导1.2 理解多元函数偏导1.3 理解微积分…

【附源码】计算机毕业设计JAVA疫情下智慧社区系统

【附源码】计算机毕业设计JAVA疫情下智慧社区系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA …

CorelDRAW2023最新版矢量设计软件

CorelDRAW2023最新版是我比较用的比较好的一款软件&#xff0c;因为其作为一款优秀的矢量设计软件&#xff0c;兼具功能和性能&#xff0c;它是由Corel公司出品的矢量设计工具&#xff0c;被广泛应用于排版印刷、矢量图形编辑、网页设计等行业。CDR软件的优势在于&#xff1a;易…

studio3T import a SQL Database to Mongodb(从mysql中导入数据到mongodb)

具体参考studio3T官方文档&#xff1a;Import a SQL Database to MongoDB in 5 Steps | Studio 3T 1、打开SQL Migration-->选择SQL to MongoDB Migration 2、创建源数据库的连接&#xff08;本文源数据库是mysql&#xff09; 3、选择目标数据库 默认选择当前连接的数据库…

深度学习入门(6)误差反向传播基础---计算图与链式法则

在我的第三篇博文《深度学习入门&#xff08;3&#xff09;神经网络参数梯度的计算方式》中详细介绍了通过微分方式计算神经网络权重参数的梯度。但是数值微分的方式计算梯度效率较低。后续博文会介绍另外一种更加高效的梯度计算方式---误差的反向传播。 这篇文章介绍的是误差…

新知实验室 腾讯云实时音视频 RTC WEB端初识

这里写目录标题前言初识产品产品介绍基础功能高级功能扩展功能快速上手位置创建源码下载源码文档写入密钥使用调试区域前言 当前时代是信息行业飞速发展的时代&#xff0c;万物都在朝物联网方向转化。而人作为一个意识体&#xff0c;也正在通过互联网&#xff0c;认识一个全新…

Design Compiler工具学习笔记(6)

目录 引言 知识储备 实际操作 设计源码 仿真源码 VCS执行仿真 DC 综合 引言 本篇继续学习 DC的基本使用。本篇主要学习 DC 综合之后的效果分析&#xff0c;重点在时序分析。 前文链接&#xff1a; Design Compiler工具学习笔记&#xff08;1&#xff09; Design Comp…

cubeIDE开发, stm32的OLED点亮及字符显示设计(基于SPI通信)

一、SPI 通信技术 显示屏&#xff08;LCD、OLED&#xff09;接口一般有I2C、SPI、UART、RGB、LVDS、MIPI、EDP和DP等。一般3.5寸以下的小尺寸LCD屏&#xff0c;显示数据量比较少&#xff0c;普遍采用低速串口&#xff0c;如I2C、SPI、UART。SPI&#xff08;Serial Peripheral I…

css-实现卡牌的发牌和翻转动画

场景描述&#xff1a; 打开抽卡界面&#xff0c;卡牌出现并发牌至固定的位置&#xff0c;此时展示的是卡牌的背面&#xff1b;用户点击卡牌时&#xff0c;卡牌进行翻转&#xff0c;并展示卡牌内容&#xff0c;或者发牌后自动进行翻转和展示。 本实例在页面挂载后自动播放动画&…

systemd 252 如预期的锁定了 Linux 引导过程

导读今天给大家介绍一下systemd 252锁定 Linux 引导过程systemd 252 如预期的锁定了 Linux 引导过程 之前&#xff0c;我们 报道 过&#xff0c;systemd 创始人发文指出 Linux 引导过程不安全&#xff0c;并提出采用加密签名的统一内核镜像&#xff08;UKI&#xff09;&#x…

linux搭建git服务器,windows客户端配置git

Linux服务器配置之Git服务器搭建步骤&#xff1a; 一、配置环境 1、服务器&#xff1a;CentOS 8.2&#xff08;64位&#xff09; Git &#xff08;version 2.27.0&#xff09; 2、客户端&#xff1a;Windows 10 &#xff08;64位&#xff09; Git&#xff08;version 2.38.…

Actipro Windows Forms Controls 22.1.3 注册版

Actipro Windows Forms Controls 窗体控件 一组用于构建漂亮的 Windows 窗体桌面应用程序的 UI 控件 语法编辑器 语法高亮代码编辑器控件和解析套件。 为您自己的应用程序带来类似于 Visual Studio 的强大代码编辑体验&#xff0c;以及流行代码编辑器中的所有高级功能。大多数流…

C#程序采用AOT发布,真的可以避免被反编译?

上次跟大家分享过&#xff0c;C#程序反编译与篡改代码的教程《C#程序发布时&#xff0c;一定要好好的保护&#xff0c;不然你会后悔的&#xff01;》&#xff0c;根据这个教程&#xff0c;我们都知道C#程序&#xff0c;发布后必须进行加密混淆&#xff0c;不然就是相当于源码直…

Redis基础命令(set类型)交集并集差集

目录 概述: 特征&#xff1a; Set常见命令&#xff1a; 1.Sadd key number..&#xff1a;向set中添加一个或多个元素 2.Srem key number...&#xff1a;移除set中指定的元素 3.Scard key&#xff1a;返回set中元素的个数 4.Sismember key member&#xff1a;判断一个元素…

html实现好看的导航主页(附源码)

文章目录1.设计来源1.1 主界面1.2 底部导航1.3 屏幕保护2.效果和源码2.1 动态效果2.2 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/128028326 html实现好看的导航主页(附源码) html实现好看的导航主页&…

小小王总,如何变成任正非、化腾、强东这样的巨人!

原创&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;&#xff0c;欢迎分享&#xff0c;非公众号转载保留此声明。王总特别迷信外面的企业培训。当遇到问题时&#xff0c;他喜欢去取经。这个经不像唐僧取经一样&#xff0c;需要历经九九八十一难…

谱本征正交分解 (SPOD)附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …