当前位置: 首页 > news >正文

【黑马点评】 使用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开销帧结构

开销是为了保证净荷正常、灵活传送所必须附加的供网络运行、管理和维护&#xff08;OAM&#xff09;使用的字节。 OTN电层开销包括OTUk开销、ODUk开销、OPUk开销、OTUCn开销、ODUCn开销、OPUCn开销和帧对齐开销。 SM开销属于OTU开销&#xff0c;占用3个字节&#xff1b;PM开销…...

Vim基本用法

Vim用法 一、基本模式 1. 普通模式&#xff08;Normal Mode&#xff09; 移动光标 基本移动&#xff1a;使用方向键&#xff08;h左移、j下移、k上移、l右移&#xff09;&#xff0c;也可以使用 H&#xff08;移到屏幕顶部&#xff09;、M&#xff08;移到屏幕中间&#xff…...

python 实现Tarjan 用于在有向图中查找强连通分量的算法

Tarjan 用于在有向图中查找强连通分量的算法介绍 Tarjan算法是一种用于在有向图中查找强连通分量的高效算法&#xff0c;由Robert Tarjan在1972年提出。强连通分量是指在有向图中&#xff0c;如果从顶点u到顶点v以及从顶点v到顶点u都存在一条路径&#xff0c;那么顶点u和顶点v…...

Qt开发技巧(十五)字符串去除空格,跨网段搜索不生效,设置图片显示失败问题,表格视图的批量删除,主动判断字串编码,开启向前查询的属性,画家类载入html来绘制

继续讲一些Qt开发中的技巧操作&#xff1a; 1.字符串去除空格 我们经常会遇到字符串重去除空格的情况&#xff0c;对于QString去除空格&#xff0c;有多种场景&#xff0c;可能需要去除左侧、右侧、所有等位置的空格&#xff1b; //字符串去空格 -1移除左侧空格 0移除所有空格…...

【机器学习】智驭未来:探索机器学习在食品生产中的革新之路

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f50d;1. 引言&#xff1a;探索机器学习在食品生产中的革新之路&#x1f4d2;2. 机器学习在食品质量控制中的应用&#x1f31e;实…...

Ubuntu 安装CUDA并使用Docker配置Pytorch环境

文章目录 参考安装顺序Nvidia GPU driverDockerNvidia Container ToolkitDocker PyTorch 1. Nvidia GPU Driver2. Docker 安装&#xff08;使用apt存储库进行安装&#xff09;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是一个基于大型语言模型的增量知识图谱构建工具&#xff0c;通过从文本文档中提取实体和关系来逐步构建知识图谱。该工具具有零样本学习能力&#xff0c;能够在无需特定训练的情况下&#xff0c;在多个领域中进行知识提取。它包括文档提炼、实体提取和关系提取模块&…...

React基础-快速梳理

React介绍 React由Meta公司开发&#xff0c;是一个用于构建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&#xff1a;Fault to Tolerance 允许故障数 故障域&#xff1a;每一台vSAN主机是一个故障域 - 假设3台超融合&#xff08;3计算1存储&#xff09;&…...

图解C#高级教程(四):协变、逆变

本章的主题是可变性&#xff08;variance&#xff09;&#xff0c;这里的可变性更多的是指基类和派生类之间的转换。可变性分为三种&#xff1a;协变&#xff08;covariance&#xff09;、逆变&#xff08;contravariance&#xff09;和不变&#xff08;invariance&#xff09;…...

详解CSS中的伪元素

4.3 伪元素 可以把样式应用到文档树中根本不存在的元素上。 ::first-line 文本中的第一行 ::first-letter 文本中的第一个字母 ::after 元素之后添加 ::before 元素之前 代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8&q…...

paper_template

paper_template Title 文章标题 Abstract 摘要 Keywords 关键词 Highlights Highlights / 创新点 Summary 写完笔记之后最后填&#xff0c;概述文章的内容&#xff0c;以后查阅笔记的时候先看这一段。 Backgrounds 描述当前研究背景 Research Objective 作者的研…...

【Bug】解决 Ubuntu 中 “error: Unable to Find Python3 Executable” 错误

解决 Ubuntu 中 “Unable to Find Python3 Executable” 错误 在 Ubuntu 系统上使用 Python 进行开发时&#xff0c;遇到找不到 python3 可执行文件的错误。 主要问题是无法正常打开终端&#xff08;原生与terminator&#xff09;&#xff0c;找不到python3&#xff0c;且无法…...

CUDA与TensorRT学习六:模型部署-CNN、模型部署-YOLOv8检测器、部署BEVFusion模型

文章目录 一、模型部署-CNN二、模型部署-YOLOv8检测器三、部署BEVFusion模型 一、模型部署-CNN 二、模型部署-YOLOv8检测器 三、部署BEVFusion模型...

防sql注入的网站登录系统设计与实现

课程名称 网络安全 大作业名称 防sql注入的网站登录系统设计与实现 姓名 学号 班级 大 作 业 要 求 结合mysql数据库设计一个web登录页面密码需密文存放&#xff08;可以采用hash方式&#xff0c;建议用sha1或md5加盐&#xff09;采用服务器端的验证码&#…...

如何快速切换电脑的ip地址

在当今的数字化时代&#xff0c;IP地址作为网络身份的重要标识&#xff0c;其重要性日益凸显。无论是出于保护个人隐私的需要&#xff0c;还是为了访问特定的网络服务等&#xff0c;快速切换电脑的IP地址已成为许多用户的迫切需求。本文将为你介绍几种实用的方法&#xff0c;帮…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...