返回顶部
首页 > 资讯 > 后端开发 > Python >springboot +rabbitmq+redis实现秒杀示例
  • 117
分享到

springboot +rabbitmq+redis实现秒杀示例

2024-04-02 19:04:59 117人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录实现说明1、工具准备2、数据表3、pom4、代码结构5、配置config6、订单业务层7、Redis实现层8、MQ实现层9、redis模拟初始化库存量10、controller控

实现说明

这里的核心在于如何在大并发的情况下保证数据库能扛得住压力,因为大并发的瓶颈在于数据库。如果用户的请求直接从前端传到数据库,显然,数据库是无法承受几十万上百万甚至上千万的并发量的。因此,我们能做的只能是减少对数据库的访问。例如,前端发出了100万个请求,通过我们的处理,最终只有10个会访问数据库,这样就会大大提升系统性能。再针对秒杀这种场景,因为秒杀商品的数量是有限的,因此采用上述实现方案。

假如,某个商品可秒杀的数量是10,那么在秒杀活动开始之前,把商品的ID和数量加载到Redis缓存。当服务端收到请求时,首先预减Redis中的数量,如果数量减到小于0时,那么随后的访问直接返回秒杀失败的信息。也就是说,最终只有10个请求会去访问数据库。

如果商品数量比较多,比如1万件商品参与秒杀,那么就有1万*10=10万个请求并发去访问数据库,数据库的压力还是会很大。这里就用到了另外一个非常重要的组件:消息队列。我们不是把请求直接去访问数据库,而是先把请求写到消息队列中,做一个缓存,然后再去慢慢的更新数据库。这样做之后,前端用户的请求可能不会立即得到响应是成功还是失败,很可能得到的是一个排队中的返回值,这个时候,需要客户端去服务端轮询,因为我们不能保证一定就秒杀成功了。当服务端出队,生成订单以后,把用户ID和商品ID写到缓存中,来应对客户端的轮询就可以了。这样处理以后,我们的应用是可以很简单的进行分布式横向扩展的,以应对更大的并发。当然,秒杀系统还有很多要处理的事情,比如限流防刷、分布式Session等等。

1、工具准备

RabbitMQ安装:https://www.jb51.net/article/253706.htm

界面地址:Http://localhost:15672/#/
用户名 guest
密码 guest

redis安装:https://www.jb51.net/article/145704.htm
jmeter安装:https://www.jb51.net/article/232152.htm

2、数据表

商品表

-- ----------------------------
-- Table structure for stock
-- ----------------------------
DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
  `count` int(11) NOT NULL COMMENT '库存',
  `sale` int(11) NOT NULL COMMENT '已售',
  `version` int(11) NOT NULL COMMENT '乐观,版本号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

订单表

-- ----------------------------
-- Table structure for stock_order
-- ----------------------------
DROP TABLE IF EXISTS `stock_order`;
CREATE TABLE `stock_order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL COMMENT '库存ID',
  `name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名称',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3、pom

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>

 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

4、代码结构

5、配置config

mq配置

package com.yy.msserver.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
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;


@Configuration
public class MyRabbitMQConfig {

    //库存交换机
    public static final String STORY_EXCHANGE = "STORY_EXCHANGE";

    //订单交换机
    public static final String ORDER_EXCHANGE = "ORDER_EXCHANGE";

    //库存队列
    public static final String STORY_QUEUE = "STORY_QUEUE";

    //订单队列
    public static final String ORDER_QUEUE = "ORDER_QUEUE";

    //库存路由键
    public static final String STORY_ROUTING_KEY = "STORY_ROUTING_KEY";

    //订单路由键
    public static final String ORDER_ROUTING_KEY = "ORDER_ROUTING_KEY";
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2jsonMessageConverter();
    }
    //创建库存交换机
    @Bean
    public Exchange getStoryExchange() {
        return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();
    }
    //创建库存队列
    @Bean
    public Queue getStoryQueue() {
        return new Queue(STORY_QUEUE);
    }
    //库存交换机和库存队列绑定
    @Bean
    public Binding bindStory() {
        return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();
    }
    //创建订单队列
    @Bean
    public Queue getOrderQueue() {
        return new Queue(ORDER_QUEUE);
    }
    //创建订单交换机
    @Bean
    public Exchange getOrderExchange() {
        return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();
    }
    //订单队列与订单交换机进行绑定
    @Bean
    public Binding bindOrder() {
        return BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();
    }
}

redis配置

package com.yy.msserver.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    // 配置redis得配置详解
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        template.seTKEySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

6、订单业务层

接口层

package com.yy.msserver.service;

import com.yy.msserver.model.vo.Stock;


public interface StockOrderService {
    public Integer createOrder(Integer id);

    public void decrByStock(Integer id);
}

实现层

package com.yy.msserver.service.impl;

import com.yy.msserver.dao.StockMapper;
import com.yy.msserver.dao.StockOrderMapper;
import com.yy.msserver.model.vo.Stock;
import com.yy.msserver.model.vo.StockOrder;
import com.yy.msserver.service.StockOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;


@Service
public class StockOrderServiceImpl implements StockOrderService {
    @Autowired
    private StockOrderMapper stockOrderMapper;

    @Autowired
    private StockMapper stockMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer createOrder(Integer id) {
        //校验库存
        Stock stock = checkStock(id);
//        if(stock.getCount()>0){
//            System.out.println("当前库存:" + stock.getCount());
//            //扣库存
//            if(updateSale(stock) == 1){
//
//            }else {
//                return 0;
//            }
//        }
//        return 0;
        return createOrder(stock);
    }

    @Override
    public void decrByStock(Integer id){
        //校验库存
        Stock stock = checkStock(id);
        if(stock.getCount()>0){
            System.out.println("当前库存:" + stock.getCount());
            //扣库存
            updateSale(stock);
        }
    }

    //校验库存
    private Stock checkStock(Integer id) {
        return stockMapper.checkStock(id);
    }

    //扣库存
    private int updateSale(Stock stock){
        return stockMapper.updateSale(stock);
    }

    //下订单
    private Integer createOrder(Stock stock){
        StockOrder order = new StockOrder();
        order.setSid(stock.getId());
        order.setCreateTime(new Date());
        order.setName(stock.getName());
        stockOrderMapper.createOrder(order);
        return order.getId();
    }
}

7、redis实现层

package com.yy.msserver.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.TimeUnit;


@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void put(String key, Object value, long millis) {
        redisTemplate.opsForValue().set(key, value, millis, TimeUnit.MINUTES);
    }
    public void putForHash(String objectKey, String hkey, String value) {
        redisTemplate.opsForHash().put(objectKey, hkey, value);
    }
    public <T> T get(String key, Class<T> type) {
        return (T) redisTemplate.boundValueOps(key).get();
    }
    public void remove(String key) {
        redisTemplate.delete(key);
    }
    public boolean expire(String key, long millis) {
        return redisTemplate.expire(key, millis, TimeUnit.MILLISECONDS);
    }
    public boolean persist(String key) {
        return redisTemplate.hasKey(key);
    }
    public String getString(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }
    public Integer getInteger(String key) {
        return (Integer) redisTemplate.opsForValue().get(key);
    }
    public Long getLong(String key) {
        return (Long) redisTemplate.opsForValue().get(key);
    }
    public Date getDate(String key) {
        return (Date) redisTemplate.opsForValue().get(key);
    }

    
    public Long decrBy(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }
}

8、mq实现层

减库存

package com.yy.msserver.service;

import com.yy.msserver.config.MyRabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Slf4j
@Service
public class MQStockService {
    @Autowired
    private StockOrderService stockService;
    
    @RabbitListener(queues = MyRabbitMQConfig.STORY_QUEUE)
    public void decrByStock(Integer id) {
        
        log.info("减库存");
        stockService.decrByStock(id);
    }
}

下订单

package com.yy.msserver.service;

import com.yy.msserver.config.MyRabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
@Slf4j
public class MQOrderService {
    @Autowired
    private StockOrderService orderService;
    
    @RabbitListener(queues = MyRabbitMQConfig.ORDER_QUEUE)
    public void createOrder(Integer id) {
        log.info("收到订单消息");
        orderService.createOrder(id);
    }
}

9、redis模拟初始化库存量

package com.yy.msserver.config;

import com.yy.msserver.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;


@Component
public class InitConfig {
    @Autowired
    private RedisService redisService;
    
    @PostConstruct
    public void init() {
        redisService.put("1", 10, 20);
    }
}

10、controller控制层

 
    @GetMapping( value = "/sec",produces = "application/json;charset=utf-8")
    @ResponseBody
    public String sec(@RequestParam(value = "id") int id) {

        String message = null;
        //调用redis给相应商品库存量减一
        Long decrByResult = redisService.decrBy(id+"");
        if (decrByResult >= 0) {
            
            //发消息给库存消息队列,将库存数据减一
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.STORY_EXCHANGE, MyRabbitMQConfig.STORY_ROUTING_KEY, id);

            //发消息给订单消息队列,创建订单
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE, MyRabbitMQConfig.ORDER_ROUTING_KEY, id);
            message = "商品" + id + "秒杀成功";
        } else {
            
            message ="商品" + id + "秒杀商品的库存量没有剩余,秒杀结束";
        }
        return message;
    }

11、测试

新建线程组——新建取样器(http请求)——新建查看结果树

12、测试结果

到此这篇关于SpringBoot +rabbitmq+redis实现秒杀的文章就介绍到这了,更多相关springboot rabbitmq redis秒杀内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: springboot +rabbitmq+redis实现秒杀示例

本文链接: https://lsjlt.com/news/153134.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • springboot +rabbitmq+redis实现秒杀示例
    目录实现说明1、工具准备2、数据表3、pom4、代码结构5、配置config6、订单业务层7、redis实现层8、mq实现层9、redis模拟初始化库存量10、controller控...
    99+
    2024-04-02
  • SpringBoot+RabbitMQ+Redis实现商品秒杀的示例代码
    目录业务分析创建表功能实现1.用户校验2.下单3.减少库存4.支付总结业务分析 一般而言,商品秒杀大概可以拆分成以下几步: 用户校验 校验是否多次抢单,保证每个商品每个用户只能秒杀一...
    99+
    2024-04-02
  • Springboot+redis+Vue实现秒杀的项目实践
    目录1、Redis简介2、实现代码3、启动步骤4、使用ab进行并发测试5、线程安全6、总结7、参考资料1、Redis简介 Redis是一个开源的key-value存储系统。 Redi...
    99+
    2022-11-13
    Springboot+redis+Vue 秒杀 Springboot redis秒杀
  • redis秒杀系统的实现
    目录1.如何设计一个秒杀系统2.秒杀流程2.1 前端处理2.2 后端处理3.超卖问题4.总体思路1.如何设计一个秒杀系统 在设计任何系统之前,我们首先都需要先理解秒杀系统的业务背景 ...
    99+
    2024-04-02
  • 基于Redis结合SpringBoot的秒杀案例详解
    目录1、构建SpringBoot项目2、启动类3、在Controller层里定义秒杀接口4、在Service层里通过lua脚本实现秒杀效果5、配置redis连接参数6、演示秒杀效果&...
    99+
    2024-04-02
  • php中使用redis实现秒杀
    ...
    99+
    2019-10-28
    php中使用redis实现秒杀
  • 如何使用Redis实现秒杀
    如何使用Redis实现秒杀?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。导语:秒杀想必大家都了解,在短时间内请求访问会激增,同...
    99+
    2024-04-02
  • redis怎么实现秒杀功能
    在Redis中实现秒杀功能的一种常见方法是使用Redis的原子操作和事务来控制并发访问和更新库存数量。 以下是一个简单的秒杀功能的实...
    99+
    2024-04-02
  • SpringBoot之使用Redis实现分布式锁(秒杀系统)
    目录一、Redis分布式锁概念篇1.1、为什么要使用分布式锁1.2、分布式锁应具备哪些条件1.3、分布式锁的三种实现方式二、Redis分布式锁实战篇2.1、导入依赖2.2、配置Red...
    99+
    2024-04-02
  • Redis瞬时高并发秒杀的示例分析
    这篇文章给大家分享的是有关Redis瞬时高并发秒杀的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.Redis丰富的数据结构(Data Structures)字符串(S...
    99+
    2024-04-02
  • Spring+Redis+RabbitMQ开发限流和秒杀项目功能
    目录一、项目概述二、基于Redis的Lua脚本分析Lua脚本的特性在Redis中引入Lua脚本Lua脚本的返回值和参数分支语句三、实现限流和秒杀功能创建项目并编写配置文件编写启动类和...
    99+
    2024-04-02
  • SpringBoot+RabbitMQ 实现死信队列的示例
    前言 死信:无法被消费的消息,称为死信。 如果死信一直留在队列中,会导致一直被消费,却从不消费成功。 所以我们专门开辟了一个来存放死信的队列,叫死信队列(DLX,dead-lette...
    99+
    2024-04-02
  • 如何使用Redis实现秒杀功能
    这篇文章主要介绍如何使用Redis实现秒杀功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1. 怎样预防数据库超售现象设置数据库事务的隔离级别为Serializable(不可用)Serializable就是让数据库...
    99+
    2023-06-14
  • Redis消息队列怎么实现秒杀
    要实现秒杀功能,可以使用Redis的消息队列来进行异步处理。下面是一种基本的实现方法:1. 准备工作:创建一个商品库存键值对,如"s...
    99+
    2023-10-11
    Redis
  • PHP中使用Redis实现秒杀活动
    随着电商行业的发展,秒杀活动成为了各大平台吸引用户的重要方式之一。而随着用户数量的增加,原有的服务器无法承受瞬时的访问量,导致服务器崩溃,无法继续进行秒杀活动。为了解决这一问题,我们可以采用Redis进行秒杀活动的实现。Redis是一个基于...
    99+
    2023-05-16
    PHP redis 秒杀活动
  • python淘宝准点秒杀抢单的实现示例
    我这里使用的chorme浏览器,使用前购物车内需要有商品 一、ChromeDriver的安装 若想使用Selenium成功调用Chrome浏览器完成相应的操作,需要通过ChromeDriver来驱动。 这里是C...
    99+
    2022-06-02
    python 淘宝准点抢单 python自动抢单 python秒杀
  • SpringBoot怎么整合ActiveMQ实现秒杀队列
    本文小编为大家详细介绍“SpringBoot怎么整合ActiveMQ实现秒杀队列”,内容详细,步骤清晰,细节处理妥当,希望这篇“SpringBoot怎么整合ActiveMQ实现秒杀队列”文章能帮助大家解决疑...
    99+
    2024-04-02
  • Redis实现秒杀的问题怎么解决
    本篇内容介绍了“Redis实现秒杀的问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、秒杀逻辑...
    99+
    2024-04-02
  • 怎样用Redis轻松实现秒杀系统
    这篇文章将为大家详细讲解有关怎样用Redis轻松实现秒杀系统,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。秒杀系统的架构设计秒杀系统,是典型的短时大量突发访问类问题。对这类问题,有三种优化性...
    99+
    2023-06-02
  • 基于mysql乐观锁实现秒杀的示例代码
    目录说明具体实现代码实现说明 如果你的项目流量非常小,完全不用担心有并发的购买请求,那么做这样一个系统意义不大。但如果你的系统要像12306那样,接受高并发访问和下单的考验,那么你就需要一套完整的流程保护措施,来保证你系...
    99+
    2022-07-01
    mysql乐观锁秒杀 mysql秒杀
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作