【黑马点评】 使用RabbitMQ实现消息队列——2.使用RabbitMQ监听秒杀下单
2 使用RabbitMQ实现消息队列
2.1 修改\hm-dianping\pom.xmlpom.xml文件
添加RabbitMQ的环境
<!-- RabbitMQ-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.2 修改Resource下的application.yaml文件
添加RabbitMQ的配置信息
spring:rabbitmq:host: 127.0.0.1 # IP地址port: 5672 # 端口号username: hmdianping # 用户名password: 123456 # 密码listener:simple:concurrency: 1max-concurrency: 1acknowledge-mode: manualprefetch: 1
主启动类标注@EnableRabbit开启消息队列的监听功能

2.3 配置RabbitMQ,创建交换机和消息队列,将二者绑定
创建direct类型的交换机以及一个名为seckill.order.queue的消息队列,然后将二者绑定.之后发往该交换机且路由键为seckill.order的消息都会转发seckill.order.queue
具体而言,在hm-dianping.src.main.java.com.hmdp.config下新建RabbitMQConfig文件
文件内容如下
package com.hmdp.config;import com.hmdp.entity.VoucherOrder;
import com.hmdp.service.IVoucherOrderService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;
import java.io.IOException;@Configuration
public class RabbitMQConfig {@ResourceIVoucherOrderService voucherOrderService;@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.seckill.queue"),key = "direct.seckill",exchange = @Exchange(name = "hmdianping.direct", type = ExchangeTypes.DIRECT)))public void recieveMessage(Object message){System.out.println("监听到了"+message);}@Beanpublic MessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}
}
2.3.1 测试监听消息
在Test中添加发送消息的方法,指定交换机hmdianping.direct 为和路由键 direct.seckill。
@ResourceRabbitTemplate rabbitTemplate;@Testpublic void testSendMessage(){rabbitTemplate.convertAndSend("hmdianping.direct","direct.seckill","测试发送消息");}
先运行Test方法

之后运行启动类HmDianPingApplication
发现监听到了,说明连接成功。

2.3.2 修改秒杀下单业务(VoucherOrderServiceImpl中的seckillVoucher方法)
-
注入RabbitTemplate类
-
在认定有抢购资格后,直接向seckill.direct交换机发送消息,内容包含voucherId、userId、orderId
@ResourceRabbitTemplate rabbitTemplate;@Overridepublic Result seckillVoucher(Long voucherId) {//1.执行lua脚本,判断当前用户的购买资格Long userId = UserHolder.getUser().getId();Long result = stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(), userId.toString());if (result != 0) {//2.不为0说明没有购买资格return Result.fail(result==1?"库存不足":"不能重复下单");}//3.走到这一步说明有购买资格,将订单信息存到消息队列VoucherOrder voucherOrder = new VoucherOrder();long orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);voucherOrder.setUserId(UserHolder.getUser().getId());voucherOrder.setVoucherId(voucherId);//存入消息队列等待异步消费rabbitTemplate.convertAndSend("hmdianping.direct","direct.seckill",voucherOrder);return Result.ok(orderId);}
此时VoucherOrderServiceImpl文件内容如下
package com.hmdp.service.impl;import com.hmdp.dto.Result;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;/*** <p>* 服务实现类* </p>** @author 虎哥* @since 2021-12-22*/
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate ISeckillVoucherService seckillVoucherService;@Resourceprivate IVoucherOrderService iVoucherOrderService;@Resourceprivate RedisIdWorker redisIdWorker;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate RedissonClient redissonClient;private static final DefaultRedisScript<Long> SECKILL_SCRIPT;static {SECKILL_SCRIPT = new DefaultRedisScript<>();SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));SECKILL_SCRIPT.setResultType(Long.class);}private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024*1024);//异步处理线程池private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();@Transactionalpublic void handleVoucherOrder(VoucherOrder voucherOrder) {//1.所有信息从当前消息实体中拿Long voucherId = voucherOrder.getVoucherId();//2.扣减库存boolean success = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId)//======判断当前库存是否大于0就可以决定是否能抢池子中的券了.gt("stock", 0).update();//3.创建订单if(success) save(voucherOrder);}@ResourceRabbitTemplate rabbitTemplate;@Overridepublic Result seckillVoucher(Long voucherId) {//1.执行lua脚本,判断当前用户的购买资格Long userId = UserHolder.getUser().getId();Long result = stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(), userId.toString());if (result != 0) {//2.不为0说明没有购买资格return Result.fail(result==1?"库存不足":"不能重复下单");}//3.走到这一步说明有购买资格,将订单信息存到消息队列VoucherOrder voucherOrder = new VoucherOrder();long orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);voucherOrder.setUserId(UserHolder.getUser().getId());voucherOrder.setVoucherId(voucherId);//存入消息队列等待异步消费rabbitTemplate.convertAndSend("hmdianping.direct","direct.seckill",voucherOrder);return Result.ok(orderId);}@Transactionalpublic void createVoucherOrder(VoucherOrder voucherOrder){// 5.一人一单逻辑// 5.1.用户idLong userId = voucherOrder.getId();int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder).count();// 5.2.判断是否存在if (count > 0) {// 用户已经购买过了log.error("用户已经购买过一次!");return ;}//6.扣减库存boolean success = seckillVoucherService.update().setSql("stock= stock -1") // set stock = stock -1.eq("voucher_id", voucherOrder).gt("stock",0)// where id = ? and stock > 0.update();if (!success) {//扣减库存log.error("库存不足!");return ;}save(voucherOrder);}
}
2.3.3 监听秒杀成功订单
- 监听seckill.order.queue队列的信息并且创建订单到数据库,当创建完成时手动ack
RabbitMQConfig中的recieveMessage修改为
public void recieveMessage(Message message, Channel channel, VoucherOrder voucherOrder){try {voucherOrderService.handleVoucherOrder(voucherOrder);channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);} catch (IOException e) {throw new RuntimeException(e);}System.out.println("监听到了"+message);}
此时,文件内容为。
package com.hmdp.config;import com.hmdp.entity.VoucherOrder;
import com.hmdp.service.IVoucherOrderService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;
import java.io.IOException;@Configuration
public class RabbitMQConfig {@ResourceIVoucherOrderService voucherOrderService;@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.seckill.queue"),key = "direct.seckill",exchange = @Exchange(name = "hmdianping.direct", type = ExchangeTypes.DIRECT)))
/* public void recieveMessage(Object message){System.out.println("监听到了"+message);}*/public void recieveMessage(Message message, Channel channel, VoucherOrder voucherOrder){try {voucherOrderService.handleVoucherOrder(voucherOrder);channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);} catch (IOException e) {throw new RuntimeException(e);}System.out.println("监听到了"+message);}@Beanpublic MessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}
}
2.3.4 测试
使用Apifox测试

成功

相关文章:
【黑马点评】 使用RabbitMQ实现消息队列——2.使用RabbitMQ监听秒杀下单
2 使用RabbitMQ实现消息队列 2.1 修改\hm-dianping\pom.xmlpom.xml文件 添加RabbitMQ的环境 <!-- RabbitMQ--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </depe…...
业务封装与映射 -- OTUk/ODUk/OPUk开销帧结构
开销是为了保证净荷正常、灵活传送所必须附加的供网络运行、管理和维护(OAM)使用的字节。 OTN电层开销包括OTUk开销、ODUk开销、OPUk开销、OTUCn开销、ODUCn开销、OPUCn开销和帧对齐开销。 SM开销属于OTU开销,占用3个字节;PM开销…...
Vim基本用法
Vim用法 一、基本模式 1. 普通模式(Normal Mode) 移动光标 基本移动:使用方向键(h左移、j下移、k上移、l右移),也可以使用 H(移到屏幕顶部)、M(移到屏幕中间ÿ…...
python 实现Tarjan 用于在有向图中查找强连通分量的算法
Tarjan 用于在有向图中查找强连通分量的算法介绍 Tarjan算法是一种用于在有向图中查找强连通分量的高效算法,由Robert Tarjan在1972年提出。强连通分量是指在有向图中,如果从顶点u到顶点v以及从顶点v到顶点u都存在一条路径,那么顶点u和顶点v…...
Qt开发技巧(十五)字符串去除空格,跨网段搜索不生效,设置图片显示失败问题,表格视图的批量删除,主动判断字串编码,开启向前查询的属性,画家类载入html来绘制
继续讲一些Qt开发中的技巧操作: 1.字符串去除空格 我们经常会遇到字符串重去除空格的情况,对于QString去除空格,有多种场景,可能需要去除左侧、右侧、所有等位置的空格; //字符串去空格 -1移除左侧空格 0移除所有空格…...
【机器学习】智驭未来:探索机器学习在食品生产中的革新之路
📝个人主页🌹:Eternity._ 🌹🌹期待您的关注 🌹🌹 ❀目录 🔍1. 引言:探索机器学习在食品生产中的革新之路📒2. 机器学习在食品质量控制中的应用🌞实…...
Ubuntu 安装CUDA并使用Docker配置Pytorch环境
文章目录 参考安装顺序Nvidia GPU driverDockerNvidia Container ToolkitDocker PyTorch 1. Nvidia GPU Driver2. Docker 安装(使用apt存储库进行安装)3. Nvidia Container Toolkit3.1 Docker测试GPU 参考 安装顺序 Nvidia GPU driver Docker Nvidia…...
【论文阅读】Simulating 500 million years of evolution with a language model
Simulating 500 million years of evolution with a language model 1、概述 展示了语言模型在蛋白质设计和进化模拟方面的能力。通过对 ESM3 模型的研究,发现其能够生成与自然蛋白质差异较大且具有功能的新蛋白质,如新型绿色荧光蛋白(GFP),表明语言模型可以达到自然进化…...
detectron2/layers源码笔记
from .wrappers import ( BatchNorm2d, Conv2d, #在torch.conv2d的基础上集成了norm层和activation层 ConvTranspose2d, cat, interpolate, Linear, nonzero_tuple, #nonzero_tuple(x)得到tuple of 每个维度的索引 cross_entropy, empty_input_loss_func…...
LLM+知识图谱新工具! iText2KG:使用大型语言模型构建增量知识图谱
iText2KG是一个基于大型语言模型的增量知识图谱构建工具,通过从文本文档中提取实体和关系来逐步构建知识图谱。该工具具有零样本学习能力,能够在无需特定训练的情况下,在多个领域中进行知识提取。它包括文档提炼、实体提取和关系提取模块&…...
React基础-快速梳理
React介绍 React由Meta公司开发,是一个用于构建Web和原生交互界面的库 React的优势 相较于传统基于DOM开发的优势 组件化的开发方式不错的性能 相较于其它前端框架的优势 丰富的生态跨平台支持 开发环境创建 create-react-app是一个快速创建React开发环境的…...
H.264编解码 - NALU详解
一、概述 NALU(Network Abstraction Layer Unit)是H.264编解码中的一个重要概念。H.264是一种视频压缩标准,将视频数据分割成一系列的NALU。每个NALU都是一个独立的数据单元,包含视频压缩后的一个片段。每个NALU都有自己的起始码和长度前缀,用于标识NALU的起始位置和长度。…...
vSAN02:容错、存储策略、文件服务、快照与备份、iSCSI
目录 vSAN容错条带化存储策略1. 创建新策略2. 应用存储策略 vSAN文件服务文件服务快照与备份 vSAN iSCSI目标服务 vSAN容错 FTT:Fault to Tolerance 允许故障数 故障域:每一台vSAN主机是一个故障域 - 假设3台超融合(3计算1存储)&…...
图解C#高级教程(四):协变、逆变
本章的主题是可变性(variance),这里的可变性更多的是指基类和派生类之间的转换。可变性分为三种:协变(covariance)、逆变(contravariance)和不变(invariance)…...
详解CSS中的伪元素
4.3 伪元素 可以把样式应用到文档树中根本不存在的元素上。 ::first-line 文本中的第一行 ::first-letter 文本中的第一个字母 ::after 元素之后添加 ::before 元素之前 代码: <!DOCTYPE html> <html> <head><meta charset"utf-8&q…...
paper_template
paper_template Title 文章标题 Abstract 摘要 Keywords 关键词 Highlights Highlights / 创新点 Summary 写完笔记之后最后填,概述文章的内容,以后查阅笔记的时候先看这一段。 Backgrounds 描述当前研究背景 Research Objective 作者的研…...
【Bug】解决 Ubuntu 中 “error: Unable to Find Python3 Executable” 错误
解决 Ubuntu 中 “Unable to Find Python3 Executable” 错误 在 Ubuntu 系统上使用 Python 进行开发时,遇到找不到 python3 可执行文件的错误。 主要问题是无法正常打开终端(原生与terminator),找不到python3,且无法…...
CUDA与TensorRT学习六:模型部署-CNN、模型部署-YOLOv8检测器、部署BEVFusion模型
文章目录 一、模型部署-CNN二、模型部署-YOLOv8检测器三、部署BEVFusion模型 一、模型部署-CNN 二、模型部署-YOLOv8检测器 三、部署BEVFusion模型...
防sql注入的网站登录系统设计与实现
课程名称 网络安全 大作业名称 防sql注入的网站登录系统设计与实现 姓名 学号 班级 大 作 业 要 求 结合mysql数据库设计一个web登录页面密码需密文存放(可以采用hash方式,建议用sha1或md5加盐)采用服务器端的验证码&#…...
如何快速切换电脑的ip地址
在当今的数字化时代,IP地址作为网络身份的重要标识,其重要性日益凸显。无论是出于保护个人隐私的需要,还是为了访问特定的网络服务等,快速切换电脑的IP地址已成为许多用户的迫切需求。本文将为你介绍几种实用的方法,帮…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
