代码编织梦想

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 {
    @Autowired
    private 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);
    }
}
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private OrderMQSender mqSender;
    @Override
    public 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 {
   
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private GoodsService goodsService;

    /**
     * 初始化秒杀商品数量到 redis 中
     *
     * @return
     */
    @Override
    public 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("初始化完成");
    }

    @Override
    public 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";

    @Bean
    public Queue commonOrderQueue() {
        return new Queue(commonOrderQueue);
    }
    @Bean
    public TopicExchange commonExchange() {
        return new TopicExchange(commonExchange);
    }
    @Bean
    public Binding commonOrderBinding() {
        return BindingBuilder.bind(commonOrderQueue()).to(commonExchange()).with("commonOrder.#");
    }
}

然后就是订单的发送者

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

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

@Service
@Slf4j
public class OrderMQReceiver {

    @Autowired
    private 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
    @Transactional
    public 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】
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zl18603543572/article/details/129601572

java高并发秒杀平台(redis + rabbitmq)_huang zichen的博客-爱代码爱编程

Seconds-Kill 本项目是一个模拟高并发环境下基于 SpringBoot 的秒杀购物平台。为了减少对数据库的直接访问,通过 Redis 实现了缓存优化;并通过 RabbitMQ 消息中间件来接收大量的并发请求,实现

springboot+rabbitmq+redis实现商品秒杀_达摩小老头(jason)的博客-爱代码爱编程

业务分析 一般而言,商品秒杀大概可以拆分成以下几步: 用户校验 校验是否多次抢单,保证每个商品每个用户只能秒杀一次下单 订单信息进入消息队列,等待消费减少库存 消费订单消息,减少商品库存,增加订单记录付款 十五分钟内完成

springboot+mysql+redis+rabbitmq实现高并发秒杀_programer hai的博客-爱代码爱编程_rabbitmq springboot redis mysql

场景: 商城对某一商品进行秒杀活动,该项目实例中,商品为watch,库存为10,使用jemter测试工具来模拟高并发场景 代码实例: mysql表结构: 库存表:                                                                          订单表:          appl

基于springboot+redis+rabbitmq的高并发秒杀系统实现-1-爱代码爱编程

项目源码下载地址: https://github.com/wangqianlong513/springboot-redis-rabbitmq-seckill 声明: 本秒杀系统是在https://open.21ic.com/open/video/15844课程的基础上改进的。主要有如下修改 I、原版本中,springboot整合的单机版redis,

基于springboot+redis+rabbitmq的高并发秒杀系统实现-5-爱代码爱编程

项目源码下载地址: https://github.com/wangqianlong513/springboot-redis-rabbitmq-seckill 上一篇讲到秒杀过程,在后台的秒杀方法miaosha中通过sender.sendMiaoshaMessage(mm)向队列中发送了mm。 1、上面的mm是封装类MiaoshaMessage的一个实

SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。-爱代码爱编程

SpringBoot 商品下单放进RobbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。 这是一个简单的集成rabbitMQ和redis的例子项目结构pom依赖配置代码压力测试总结 这是一个简单的集成rabbitMQ和redis的例子 项目结构 pom依赖配置 <?xml version="1.0" encodin

基于SpringBoot+RabbitMQ+Redis开发的秒杀系统,实现异步下单、热点数据缓存、解决超卖等问题-爱代码爱编程

秒杀项目:SeckillProject 一、项目简介:  ​ ​ ​ ​SeckillProject基于Springboot开发的秒杀系统,实现的功能主要是登录、商品列表、商品详情、秒杀商品,订单详情等功能。在系统业务处理中,使用到分布式session维持会话、Redis预减库存降低数据库访问压力,消息队列异步下单(削峰)、客户端轮询结果、接口限流防刷

RabbitMQ基础入门&Spring整合RabbitMQ&SpringBoot整合RabbitMQ-爱代码爱编程

学习目标 能够说出什么是消息中间件能够安装 RabbitMQ能够编写 RabbitMQ 的入门程序能够说出 RabbitMQ 的5种模式特征能够使用 Spring 整合 RabbitMQMQ 的基本概念 MQ 的概述 MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 小結 M

spring boot mysql高并发_GitHub - intergrate-dev/springboot-seckill: 基于SpringBoot + MySQL + Redis + Rabb...-爱代码爱编程

系统介绍 本系统是使用SpringBoot开发的高并发限时抢购秒杀系统,除了实现基本的登录、查看商品列表、秒杀、下单等功能,项目中还针对高并发情况实现了系统缓存、降级和限流。 开发工具 IntelliJ IDEA + Navicat + Sublime Text3 + Git + Chrome 压测工具 JMeter 开发技术 前端技术

springboot+redis 实现 api 接口防刷限流_java基基的博客-爱代码爱编程

点击上方“Java基基”,选择“设为星标” 做积极的人,而不是积极废人! 每天 14:00 更新文章,每天掉亿点点头发... 源码精品专栏  原创 | Java 2021 超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Shard

基于springboot+rabbitmq+redis开发的秒杀系统(异步下单、热点数据缓存、解决超卖)_懒虫虫~的博客-爱代码爱编程

基于SpringBoot+RabbitMQ+Redis开发的秒杀系统 一、简易版秒杀SeckillProject系统简介开发技术 二、实现细节记录1、用户密码两次MD5加密2、分布式session维持会话3、异常

基于springboot的校园闲置物品交易管理系统_qq_2693978338的博客-爱代码爱编程

一、项目技术栈 开发语言:Java Java开发工具:JDK1.8 后端框架:SpringBoot 前端:采用HTML和Vue相结合开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.

基于springboot并整合rabbitmq和redis的一个支付-库存交互小demo-爱代码爱编程

前言 前段时间学习了rabbitMQ,再加上偶然看到一篇redis的并发处理小文章,就想着基于springboot把他们一起整合进来搞个练手的小东西,于是就有了这个模拟购物支付库存交互流程的demo 实现主要依靠两个核心中

基于java+springboot+vue+redis+rabbitmq的鲜花商城-爱代码爱编程

基于Java+SpringBoot+Vue+Redis+RabbitMq的鲜花商城 ✌全网粉丝20W+,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/Inf

使用java实现springboot+mysql+redis+rabbitmq+tkmybatis模拟实现高并发秒杀,新手入门-爱代码爱编程

模拟实现高并发秒杀 本篇文章所使用的技术有 **SpringBoot+Mysql+Redis+RabbitMQ+tkmybatis** 使用的工具 开发工具:IDEA redis工具:Redis Desktop M

基于rabbitmq、redis、springboot秒杀实现-爱代码爱编程

秒杀应用场景 秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。 秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。 秒杀业务流程比较简单,一般就是下订单减库存。 秒杀的业务场景跟其他业务场景不

springboot、redis轻松实现java高并发秒杀系统笔记_springboot redis处理高并发-爱代码爱编程

秒杀项目 优极限【完整项目实战】半天带你用SpringBoot、Redis轻松实现Java高并发秒杀系统 文章目录 秒杀项目技术栈课程介绍学习目标如何设计一个秒杀系统项目搭建分布式会话登录功能参数