秒杀业务是怎么做的,使用SpringBoot整合Spring-data-redis 、 rabbitMQ消息队列、redis缓存

秒杀业务是怎么做的?

在这里插入图片描述

一、前言

一提到秒杀,都会想到高性能、高并发、高可用、大流量…。在电商体系中,交易系统占据了环节中的半壁江山。比如里面特别迷人的秒杀系统,那秒杀涉及到什么架构设计?会涉及到什么业务?

二、 秒杀业务难点

(1)高并发

用户在秒杀开始前,通过不停刷新浏览器页面以保证不会错过秒杀,这些请求如果按照一般的网站应用架构,访问应用服务器、连接数据库,会对应用服务器和数据库服务器造成负载压力。

(2)超卖

由于库存并发更新的问题,导致在实际库存已经不足的情况下,库存依然在减,导致卖家的商品卖得件数超过秒杀的预期。

三、项目实现

1、项目所用技术
SpringBoot2.x框架 、 maven仓库、 rabbitMQ队列、redis缓存

技术具体
开发工具idea
所用框架SpringBoot2.2
消息中间rabbitMQ
数据库MySQL
缓存reids
高并发压力测试工具jmeter
dao层mybatis

2、项目依赖

创建数据库

创建订单表order

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80018
 Source Host           : localhost:3306
 Source Schema         : db_concurrency

 Target Server Type    : MySQL
 Target Server Version : 80018
 File Encoding         : 65001

 Date: 23/12/2019 15:41:28
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for order
-- ----------------------------
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '商品名称',
  `order_user` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

创建stock 商品表

-- ----------------------------
-- Table structure for stock
-- ----------------------------
DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '商品名',
  `stock` int(255) NULL DEFAULT NULL COMMENT '库存数量',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '商品表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of stock
-- ----------------------------
INSERT INTO `stock` VALUES (1, '小米手机', 83);

SET FOREIGN_KEY_CHECKS = 1;

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com</groupId>
    <artifactId>redis-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <includes>
                    <include>**/*Mapper.xml</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

springBoot配置文件

spring:
  rabbitmq:
    virtual-host: /
    host: localhost #rabbitMQ服务器ip
    username: guest
    password: guest
#    listener:
#      imple:
#        acknowledge-mode: manual
#  application:
#    name: concurrency-project
  redis:
    host: 192.168.25.129  #redis服务器的ip
    port: 6379
    jedis:
      pool:
        max-active: 1024
        max-wait: -1s
        max-idle: 200
    password: redis
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver #MySQLl8.0驱动
    url: jdbc:mysql://localhost:3306/db_concurrency?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true	# ?后面的参数是MySQL8.0连接必须加的,配置MySQL服务器的时区
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    druid:		#druid数据源
      initial-size: 1
      min-idle: 1
      max-active: 20
      #获取连接等待超时时间
      max-wait: 60000
      #间隔多久进行一次检测,检测需要关闭的空闲连接
      time-between-eviction-runs-millis: 60000
      #一个连接在池中最小生存的时间
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      #打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
      pool-prepared-statements: false
      max-pool-prepared-statement-per-connection-size: 20
mybatis:	#mybais配置
  mapper-locations: mapper/*.xml
  type-aliases-package: com.redisdemo.model
#pagehelper:
  ##  helper-dialect: mysql
mapper:	
  mappers: com.redisdemo.mapper #dao层包的位置
  not-empty: false
  identity: MYSQL

3、rabbitMQ配置类 MyRabbitMQConfig

package com.redisdemo.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
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;

/**
 * RabbitMQ配置类
 */
@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 RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
//        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
//        factory.setConnectionFactory(connectionFactory);
//        factory.setMessageConverter(new Jackson2JsonMessageConverter());
//        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//        return factory;
//    }
    /**
     * 创建MessageConverter对象
     * @return
     */
    @Bean
    public MessageConverter messageConverter(){
        //setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return new Jackson2JsonMessageConverter();
    }


    /**
     * 创建库存交换机
     * @return
     */
    @Bean
    public Exchange getStoryExchange(){
        //构建器
        ExchangeBuilder exchangeBuilder = ExchangeBuilder.directExchange(STORY_EXCHANGE);
        //设置是否持久 true 或者 fasle
        Exchange build = exchangeBuilder.durable(true).build();
        return build;
    }

    /**
     * 创建库存队列
     * @return 队列
     */
    @Bean
    public Queue getStoryQueue(){
        return new Queue(STORY_QUEUE);
    }

    /**
     * 库存交换机和库存队列绑定
     * @return
     */
    @Bean
    public Binding bindStory(){
        return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();
    }


    /**
     * 订单队列
     * @return
     */
    @Bean
    public Queue getOrderQueue(){
        return new Queue(ORDER_QUEUE);
    }


    /**
     * 创建订单交换机
     * @return
     */
    @Bean
    public Exchange getOrderExchange(){
        return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();
    }

    /**
     * 将订单队列和订单交换机进行绑定
     * @return
     */
    @Bean
    public Binding bindOrder(){
      return   BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();
    }
}

4、创建redis配置类

package com.redisdemo.config;

import org.springframework.amqp.core.AcknowledgeMode;
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;

/**
 * redis配置类
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

5、创建实体类

Order

package com.redisdemo.model;
import lombok.Data;

@Data //这是lombok插件的注解,如果没有的可以删除该注解,自己生成get、set方法
public class Order {

    private Integer id;

    private String orderName;

    private String orderUser;
}

Stock

package com.redisdemo.model;

import lombok.Data;

@Data //这是lombok插件的注解,如果没有的可以删除该注解,自己生成get、set方法
public class Stock {
    private Integer id;
    private String name;
    private Integer stock;
}

6、创建service

IOrderService

package com.redisdemo.service;

import com.redisdemo.model.Order;
import org.springframework.stereotype.Service;


public interface IOrderService {

    void createOrder(Order order);
}

IRedisService

package com.redisdemo.service;

import java.util.Date;

public interface IRedisService {

    /**
     * 添加到redis
     * @param key
     * @param value
     * @param milis
     */
    void put(String key,Object value,long milis);


    void putForHash(String objectKey,String hkey,String value);

    <T>T get(String key,Class type);

    void  remove(String key);

    boolean expire(String key,Long milis);

    boolean persist(String key);

    String getString(String key);

    Integer getInter(String key);

    Long getLong(String key);

    Date getDate(String key);

    Long decrBy(String key);
}

IStockService

package com.redisdemo.service;
public interface IStockService {
    void decrByStock(String stockName);

    Integer selectByExample(String stockName); 
}

IOrderServiceImpl

package com.redisdemo.service.impl;
import com.redisdemo.mapper.OrderMapper;
import com.redisdemo.model.Order;
import com.redisdemo.service.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class IOrderServiceImpl implements IOrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Override
    public void createOrder(Order order) {
        orderMapper.insert(order);
    }
}

IRedisServiceImpl

package com.redisdemo.service.impl;

import com.redisdemo.service.IRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class IRedisServiceImpl implements IRedisService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 设置string键值对
     * @param key 键
     * @param value 值
     * @param milis 时间
     */
    @Override
    public void put(String key, Object value, long milis) {
        redisTemplate.opsForValue().set(key,value,milis,TimeUnit.MINUTES);
    }

    @Override
    public void putForHash(String objectKey, String hkey, String value) {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.put(objectKey,hkey,value);
    }

    @Override
    public <T> T get(String key, Class type) {
        return (T) redisTemplate.boundValueOps(key).get();
    }

    @Override
    public void remove(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public boolean expire(String key, Long milis) {
        return redisTemplate.expire(key,milis, TimeUnit.MILLISECONDS);
    }

    @Override
    public boolean persist(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public String getString(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    @Override
    public Integer getInter(String key) {
        return (Integer) redisTemplate.opsForValue().get(key);
    }

    @Override
    public Long getLong(String key) {
        return (Long) redisTemplate.opsForValue().get(key);
    }

    @Override
    public Date getDate(String key) {
        return (Date) redisTemplate.opsForValue().get(key);
    }

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

IStockServiceImpl

package com.redisdemo.service.impl;

import com.redisdemo.mapper.StockMapper;
import com.redisdemo.model.Stock;
import com.redisdemo.service.IStockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;

@Slf4j
@Service
public class IStockServiceImpl implements IStockService {

    @Autowired
    private StockMapper stockMapper;

    @Override
    public void decrByStock(String stockName) {
        List<Stock> stockList = stockMapper.selectByExample(stockName);
        if (!CollectionUtils.isEmpty(stockList)){
            Stock stock = stockList.get(0);
            stock.setStock(stock.getStock() - 1);
            stockMapper.updateByPrimaryKey(stock);
        }
    }

    @Override
    public Integer selectByExample(String stockName) {
        List<Stock> stockList = stockMapper.selectByExample(stockName);
        if (!CollectionUtils.isEmpty(stockList)){
           return stockList.get(0).getStock();
        }
        return 0;
    }
}

package com.redisdemo.service;

import com.redisdemo.config.MyRabbitMQConfig;
import com.redisdemo.model.Order;
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 MQOrderService {

    @Autowired
    private IOrderService orderService;


    /**
     * 监听消息队列,并消费
     * @param order 订单
     */
    @RabbitListener(queues = MyRabbitMQConfig.ORDER_QUEUE)
    public void createOrder(Order order){
            log.info("收到订单消息,订单用户为{},商品名称名称为:{}",order.getOrderUser(),order.getOrderName());
            //调用数据库orderService创建订单信息
            orderService.createOrder(order);
    }
}

MQStockService

package com.redisdemo.service;

import com.redisdemo.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 IStockService iStockService;


    @RabbitListener(queues = MyRabbitMQConfig.STORY_QUEUE)
    public void decrByStock(String stockName){
        log.info("库存消息队列收到的消息商品信息是:{}",stockName);
        //调用数据库service给数据库对应商品库存减一
        iStockService.decrByStock(stockName);
    }

}

7、创建mapper层

OrderMapper

package com.redisdemo.mapper;

import com.redisdemo.model.Order;

public interface OrderMapper {


    void insert(Order order);
}

StockMapper

package com.redisdemo.mapper;

import com.redisdemo.model.Stock;

import java.util.List;

public interface StockMapper {

    /**
     * 查询商品
     * @param name 商品名
     * @return
     */
    List<Stock> selectByExample(String name);

    /**
     * 商品减库存-
     * @param stock 商品对象
     */
    void updateByPrimaryKey(Stock stock);
}

创建mybatis框架的xml映射文件

在resources目录下创建文件夹mapper
在这里插入图片描述
OrderMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
        "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<!-- mapper 为根元素节点, 一个namespace对应一个dao -->
<mapper namespace="com.redisdemo.mapper.OrderMapper">
    <insert id="insert" parameterType="com.redisdemo.model.Order">
            INSERT INTO `db_concurrency`.`order`( `order_name`, `order_user`) VALUES ( #{orderName}, #{orderUser});
    </insert>
</mapper>

StockMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
        "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<!-- mapper 为根元素节点, 一个namespace对应一个dao -->
<mapper namespace="com.redisdemo.mapper.StockMapper">
        <!--查询商品名称-->
    <select id="selectByExample" resultType="com.redisdemo.model.Stock">
         SELECT
            stock.id,
            stock.`name`,
            stock.stock
            FROM
            stock where name = #{name}
    </select>


<!-- 商品减库存-->
    <update id="updateByPrimaryKey" parameterType="com.redisdemo.model.Stock">
                UPDATE `db_concurrency`.`stock` SET  `stock` = #{stock} WHERE `id` = #{id};
    </update>
</mapper>

8、controll层

package com.redisdemo.controller;

import com.redisdemo.config.MyRabbitMQConfig;
import com.redisdemo.model.Order;
import com.redisdemo.service.IOrderService;
import com.redisdemo.service.IRedisService;
import com.redisdemo.service.IStockService;
import com.redisdemo.service.MQStockService;
import com.sun.org.apache.xpath.internal.operations.Or;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@Controller
public class SecController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private IRedisService redisService ;


    @Autowired
    private IOrderService orderService;

    @Autowired
    private MQStockService mqStockService;

    @Autowired
    private IStockService stockService;

    /**
     * 使用redis+消息队列进行秒杀实现
     * @param username
     * @param stockName
     * @return
     */
    @RequestMapping("/a")
    @ResponseBody
    public String sec(String username,String stockName){
        log.info("参加秒杀的用户是:{},秒杀的商品是:{}",username,stockName);
        String message = null;
        Long decrBy = redisService.decrBy(stockName);
        if (decrBy >= 0){
            //说明该商品的库存量有剩余,可以进行下单操作
            log.info("用户:{}秒杀该商品:{}库存有余,可以进行订单操作",username,stockName);
            //发消息给订单消息队列,创建订单
            Order order = new Order();
            order.setOrderName(stockName);
            order.setOrderUser(username);
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE,MyRabbitMQConfig.ORDER_ROUTING_KEY,order);
            message = "用户" + username + "秒杀" + stockName + "成功";
        }else {
            //说明该商品的库存没有剩余,直接返回秒杀失败的消息给用户
            log.info("用户:{}秒杀商品的库存存量没有剩余,秒杀结束",username);
            message = username + "商品的存量没有剩余,秒杀结束";
        }
        return message;
    }

    @GetMapping("/b")
    @ResponseBody
    public String secDataBase(String username,String stockName){
        log.info("参加秒杀的用户是:{},秒杀的商品是:{}",username,stockName);
        String message = null;
        //查找商品库存
        Integer stockCount = stockService.selectByExample(stockName);
        log.info("用户:{}参加秒杀,当前商品库存量是:{}",username,stockCount);
        if (stockCount > 0){
            //库存减一
            stockService.decrByStock(stockName);
            //下订单
            Order order = new Order();
            order.setOrderUser(username);
            order.setOrderName(stockName);
            orderService.createOrder(order);
            log.info("用户:{}参加秒杀结果是:成功",username);
            message = username + "参加秒杀结果是:成功";

        }else {
            log.info("用户:{}参加秒杀结果是:秒杀已经结束",username);
            message = username + "参加秒杀活动结果是:秒杀已经结束";
        }
        return message;
    }


}

9、在启动类添加方法

package com.redisdemo;

import com.redisdemo.service.IRedisService;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.annotation.MapperScans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
@MapperScan("com.redisdemo.mapper")
public class RedisDemoApplication implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(RedisDemoApplication.class, args);
    }

    @Autowired
    private IRedisService redisService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("初始化数据库成功");
        redisService.put("watch", 100, 20);
    }
}

10、使用jmeter进行模拟多人同时秒杀
安装请看博客
推荐教程博客 :Jmeter压力测试工具安装及使用教程 作者roundlight

创建测试计划
在这里插入图片描述
在这里插入图片描述
点击启动,项目日志打印:

redis数据库数据:

在这里插入图片描述

jemeter 察看结果树:

在这里插入图片描述

至此,这里使用了redis+rabbitmq实现了高并发秒杀场景,并有效的防止了超卖现象,同时听过使用纯数据库操作产生了超卖的现象。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值