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

SpringBoot 结合RabbitMQ与Redis实现商品的并发下单【SpringBoot系列12】

SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。
程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发

1 项目准备

  1. SpringBoot 整合 RabbitMQ 消息队列【SpringBoot系列11】本文章 基于这个项目来开发

本文章是系列文章 ,每节文章都有对应的代码,每节的源码都是在上一节的基础上配置而来,对应的视频讲解课程正在火速录制中。

订单系统,用户下单,即要保存即时性,也要保证流畅性,同时还要防止超卖,本文章是基于 RabbitMQ 消息队列 + Redis 实现的下单,当然后续还会的秒杀系统设计 以及后续的微服务以及熔断控制等等

如这里 我的商品 库存有 10 个
在这里插入图片描述
然后我使用 apache-jmeter-5.5 压测,200个用户1秒内请求完成,每个用户请求2次,也就是1秒有400次下单请求
在这里插入图片描述
测试完成后,商品库存为0,然后订单生成10个,完美解决并发问题
在这里插入图片描述
这是实现的普通订单,基本实现逻辑是
1、redis 校验库存,预下单
2、消息队列减库存 生成 订单 (数据库、redis、es)
3、用户查询到订单成功,发起支付
4、支付回调 修改订单数据 (数据库、redis 、es)

1 预下单接口

@Api(tags="订单模块")
@RestController()
@RequestMapping("/orders")
@Slf4j
public class OrderController {@Autowiredprivate OrderService orderService;/*** 下单* @param goodsId 商品ID* @param userId* @return*/@GetMapping("/create/{id}")public R createOrder(@PathVariable("id") Long goodsId,@RequestHeader Long userId) {return orderService.createPreOrder(goodsId,userId);}
}
    @Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate OrderMQSender mqSender;@Overridepublic R createPreOrder(Long goodsId, Long userId) {log.info("预下单处理 userId:{} goodsId:{} ",userId,goodsId);//获取redis中的商品库存 先判断商品是否有库存Boolean aBoolean = redisTemplate.hasKey("goodStock:" + goodsId);if(Boolean.FALSE.equals(aBoolean)){return R.error("下单失败 商品库存不足");}//获取商品库存int goodsStock = Integer.valueOf(redisTemplate.opsForValue().get("goodStock:" +goodsId).toString());if(goodsStock==0){return R.error("下单失败 商品库存不足");}//发送下单消息SecKillMessage message = new SecKillMessage(userId, goodsId);mqSender.sendCommonOrderMessage(JsonUtils.toJson(message));return R.okData("预下单成功");}

redisTemplate 的 hasKey 可以直接判断key是否存在,在这里如果商品的key不存在,则商品无库存,redis 的商品库存是在服务启动后,自动同步进入的

@Service
@Slf4j
public class OrderServiceImpl implements OrderService , InitializingBean {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate GoodsService goodsService;/*** 初始化秒杀商品数量到 redis 中** @return*/@Overridepublic R startSeckillInit() {List<SeckillGoods> goods = secKillGoodsService.findAllSecKillGoods();if (CollectionUtils.isEmpty(goods)) {return R.error("无秒杀商品");}goods.forEach(g -> {log.info("初始化秒杀商品 goodsId:{} stock: {}", g.getGoodsId(), g.getStockCount());redisTemplate.opsForValue().set("goodStock:" + g.getGoodsId(), g.getStockCount());});return R.ok("初始化完成");}@Overridepublic void afterPropertiesSet() throws Exception {this.startSeckillInit();}

InitializingBean 当一个类实现这个接口之后,Spring启动后,初始化Bean时,若该Bean实现InitialzingBean接口,会自动调用afterPropertiesSet()方法,完成一些用户自定义的初始化操作。

2 消息队列的定义

在这里单独定义普通下单使用的队列与交换机


import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class OrderRabbitMQTopicConfig {private static final String commonOrderQueue = "commonOrderQueue";private static final String commonExchange = "commonOrderExchange";@Beanpublic Queue commonOrderQueue() {return new Queue(commonOrderQueue);}@Beanpublic TopicExchange commonExchange() {return new TopicExchange(commonExchange);}@Beanpublic Binding commonOrderBinding() {return BindingBuilder.bind(commonOrderQueue()).to(commonExchange()).with("commonOrder.#");}
}

然后就是订单的发送者

@Service
@Slf4j
public class OrderMQSender {@Autowiredprivate RabbitTemplate rabbitTemplate;/*** 普通订单走的队列* @param msg*/public void sendCommonOrderMessage(String msg) {log.info("预下单发送消息:{}", msg);rabbitTemplate.convertAndSend("commonOrderExchange", "commonOrder.message", msg);}
}

然后定义普通订单的消息接收者

@Service
@Slf4j
public class OrderMQReceiver {@Autowiredprivate OrderService orderService;@RabbitListener(queues = "commonOrderQueue")public void receiveCommonOrderMessage(String message) {log.info("接收的秒杀订单消息:{}", message);SecKillMessage secKillMessage = JsonUtils.toObj(message, SecKillMessage.class);Long userId = secKillMessage.getUserId();Long goodsId = secKillMessage.getGoodsId();//普通下单orderService.createOrder(goodsId, userId);}}

普通下单里,就是减库存,生成订单的过程

    @Override@Transactionalpublic R createOrder(Long goodsId, Long userId) {log.info("下单处理 userId:{} goodsId:{} ",userId,goodsId);//查询商品详情Goods goods = goodsService.findGoods(goodsId);//商品的实际库存if (goods.getGoodsStock() < 1) {// 设置该商品库存为空redisTemplate.opsForValue().set("goodStock:" + goods.getId(), "0");log.info("库存不足 下单失败");return R.error("商品库存不足");}//减库存 int currentStock = goods.getGoodsStock() -1;//更新数据库 库存goods.setGoodsStock(currentStock);int update = goodsService.updateGoodsStock(goods);if(update<=0){log.info("更新库存失败 下单失败");return R.error("商品库存不足");}//更新redis 缓存redisTemplate.opsForValue().set("goodStock:" + goods.getId(), currentStock);// 下订单Order order = new Order();order.setUserId(userId);order.setGoodsId(goodsId);order.setDeliveryAddrId(0L);order.setGoodsName(goods.getGoodsName());order.setGoodsCount(1);order.setGoodsPrice(goods.getGoodsPrice());order.setOrderChannel(1);order.setStatus(0); // 订单创建中order.setCreateDate(new Date());orderMapper.insert(order);log.info("下单成功 userId:{} goodsId:{} orderId:{}",userId,goodsId,order.getId());//缓存普通订单redisTemplate.opsForValue().set("order:" +userId + ":" + goodsId, order);//保存数据到ES中//后续实现return R.okData(order);}

本文章是系列文章 ,每节文章都有对应的代码,每节的源码都是在上一节的基础上配置而来,对应的视频讲解课程正在火速录制中。

本文章只有核心代码,全部代码请查看对应源码
项目源码在这里 :https://gitee.com/android.long/spring-boot-study/tree/master/biglead-api-10-seckill
有兴趣可以关注一下公众号:biglead


  1. 创建SpringBoot基础项目
  2. SpringBoot项目集成mybatis
  3. SpringBoot 集成 Druid 数据源【SpringBoot系列3】
  4. SpringBoot MyBatis 实现分页查询数据【SpringBoot系列4】
  5. SpringBoot MyBatis-Plus 集成 【SpringBoot系列5】
  6. SpringBoot mybatis-plus-generator 代码生成器 【SpringBoot系列6】
  7. SpringBoot MyBatis-Plus 分页查询 【SpringBoot系列7】
  8. SpringBoot 集成Redis缓存 以及实现基本的数据缓存【SpringBoot系列8】
  9. SpringBoot 整合 Spring Security 实现安全认证【SpringBoot系列9】
  10. SpringBoot Security认证 Redis缓存用户信息【SpringBoot系列10】
  11. SpringBoot 整合 RabbitMQ 消息队列【SpringBoot系列11】

相关文章:

SpringBoot 结合RabbitMQ与Redis实现商品的并发下单【SpringBoot系列12】

SpringCloud 大型系列课程正在制作中&#xff0c;欢迎大家关注与提意见。 程序员每天的CV 与 板砖&#xff0c;也要知其所以然&#xff0c;本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 1 项目准备 SpringBoot 整合 RabbitMQ 消息队…...

【python进阶】序列切片还能这么用?切片的强大比你了解的多太多

&#x1f4da;引言 &#x1f64b;‍♂️作者简介&#xff1a;生鱼同学&#xff0c;大数据科学与技术专业硕士在读&#x1f468;‍&#x1f393;&#xff0c;曾获得华为杯数学建模国家二等奖&#x1f3c6;&#xff0c;MathorCup 数学建模竞赛国家二等奖&#x1f3c5;&#xff0c…...

[数据结构]直接插入排序、希尔排序

文章目录排序的概念和运用排序的概念排序运用常见的排序算法常见的排序算法直接插入排序希尔排序性能对比排序的概念和运用 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操…...

CNN、LeNet、AlexNet、VGG、GoogLeNet、RCNN、Fast RCNN、Faster RCNN、YOLO、YOLOv2、SSD等的关系

卷积神经网络的现状1943年美国数学家提出人工智能1949年心理学家建立神经元模型1957年弗兰克提出 感知器人工神经网络模型1980年建立多层感知器模型1984日本学者提出卷积神经网络原始模型神经感知机1998年提出LeNet-5卷积神经网络&#xff0c;并发展了其在音符和字符上的优势20…...

IO-day1-(fscanf、fprintf.........)

作业一、有一个usr.txt的文件&#xff0c;其中存储着用户的账户和密码&#xff0c;格式如下&#xff1a;zhangsan aaaalisi bbbbb空格前面是账户&#xff0c;空格后面是密码&#xff0c;一行一个账户、密码要求如下&#xff1a;从终端获取一个账户名和密码判断是否能够登录成功…...

C++类和对象(上篇)

目录 1.类的定义 2.类的访问限定符及封装 2.1类的访问限定符 2.2封装 3.类的作用域 4.类的实例化 5.类的大小 6.this 指针 1.类的定义 class className {// 类体&#xff1a;由成员函数和成员变量组成}; // 一定要注意后面的分号 class为定义类的关键字&#xff0c;Clas…...

解决Xshell无法连接Kali Linux 2020.1(2019.3)版本

使用Xshell远程终端工具连接虚拟机的Kali Linux却提示连接不上原因&#xff1a;Kali Linux默认没有打开SSH远程登录&#xff0c;SSH就是一种网络协议&#xff0c;用于加密的远程登录&#xff0c;所以在没有打开SSH协议之前是无法使用Xshell连接Kali Linux的。解决办法&#xff…...

项目文章 | 缓解高胆固醇血症 ,浒苔多糖如何相助?

文章标题&#xff1a;Polysaccharides from Enteromorpha prolifera alleviate hypercholesterolemia via modulating the gut microbiota and bile acid metabolism 发表期刊&#xff1a;Food & Function 影响因子&#xff1a;6.317 作者单位&#xff1a;福建医科大…...

Linux使用宝塔面板搭建网站,并内网穿透实现公网访问

文章目录前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4.固定http地址5. 配置二级子域名6.创建一个测试页面前言 宝塔面板作为简单好用的服务器运维管理面板&#xff0c;它支持Linux/Windows系统&#xff0c;我们可用它来一键配置LAMP/LNMP环境、网站、数据库、FTP等&…...

基于深度学习方法与张量方法的图像去噪相关研究

目录 1 研究现状 1.1 基于张量分解的高光谱图像去噪 1.2 基于深度学习的图像去噪算法 1.3 基于深度学习的高光谱去噪 1.4 小结 2 基于深度学习的图像去噪算法 2.1 深度神经网络基本知识 2.2 基于深度学习的图像去噪网络 2.3 稀疏编码 2.3.1 传统稀疏编码 2.3.2 群稀…...

Java基础知识之HashMap的使用

一、HashMap介绍 HashMap是Map接口的一个实现类&#xff08;HashMap实现了Map的接口&#xff09;&#xff0c;它具有Map的特点。HashMap的底层是哈希表结构。 Map是用于保存具有映射关系的数据集合&#xff0c;它具有双列存储的特点&#xff0c;即一次必须添加两个元素&#xf…...

面试--每日一经

操作系统 死锁 死锁&#xff1a;是指两个或两个以上的进程在执行过程中&#xff0c;由于竞争资源或者由于彼此通信而造成的一种阻塞的现象&#xff0c;若无外力作用&#xff0c;它们都将无法推进下去。   死锁的四个必要条件 互斥条件&#xff1a;一个资源每次只能被一个进…...

JavaSE进阶之(十六)枚举

十六、枚举16.1 背景16.2 枚举类型16.3 EnumSet 和 EnumMap01、EnumSet02、EnumMap16.1 背景 在 Java 语言中还没有引入枚举类型之前&#xff0c;表示枚举类型的常用模式是声明一组 int 类型的常量&#xff0c;常常用的就是&#xff1a; public static final int SPRING 1; …...

全同态加密:TFHE

参考文献&#xff1a; Cheon J H, Stehl D. Fully homomophic encryption over the integers revisited[C]//Advances in Cryptology–EUROCRYPT 2015: 34th Annual International Conference on the Theory and Applications of Cryptographic Techniques, Sofia, Bulgaria, …...

【计算机二级】综合题目

计算机二级python真题 文章目录计算机二级python真题一、《大学慕课 两问 》二、综合应用题——价值链三、基本操作题 ——信息输出一、《大学慕课 两问 》 附件中的文件data.txt 是教育部爱课程网中国大学MOOC平台的某个 HTML页面源文件,里面包含了我国参与MOOC建设的一批大学…...

初识Kafka

介绍 Kafka Kafka 是一款基于发布与订阅的消息系统。 用生产者客户端 API 向 Kafka 生产消息&#xff0c;用消费者客户端 API 从 Kafka 读取这些消息。 Kafka 使用 Zookeeper 保存元数据信息。 Kafka 0.9 版本之前&#xff0c;除了 broker 之外&#xff0c; 消费者也会使用…...

【JavaEE】线程的状态

哈喽&#xff0c;大家好~我是保护小周ღ&#xff0c;本期为大家带来的是 Java 多线程的 线程的状态&#xff0c;New 新建状态&#xff0c;Runnable 运行状态&#xff0c;Blocked 阻塞状态&#xff0c;waiting 等待状态&#xff0c;Time_Waiting 超时等待状态&#xff0c;Termin…...

7个最受瞩目的 Python 库,提升你的开发效率

当今时代&#xff0c;数据分析和处理已经成为了各行各业中不可或缺的一环。Python作为一种非常流行的编程语言&#xff0c;为我们提供了许多强大的工具和库来处理不同类型的数据。 在这篇文章中&#xff0c;我将向您介绍七个非常有用的Python库&#xff0c;这些库各自有着独特…...

这些IT行业趋势,将改变2023

上一周&#xff0c;你被"AI"刷屏了吗&#xff1f; 打开任何一家科技媒体&#xff0c;人工智能都是不变的热门话题。周初大家还在用ChatGPT写论文、查资料、写代码&#xff0c;到周末的时候大家已经开始用GPT-4图像识别来做饭、Microsoft 365 Copilot 来写PPT了。 GP…...

蓝桥杯每日一真题——[蓝桥杯 2021 省 B] 杨辉三角形(二分+规律)

文章目录[蓝桥杯 2021 省 B] 杨辉三角形题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路&#xff1a;全部代码&#xff1a;[蓝桥杯 2021 省 B] 杨辉三角形 题目描述 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列&…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...