使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀

使用SpringBoot+Mysql+Redis+RabbitMQ+Jmeter实现高并发秒杀

什么是高并发

高并发(High Concurrency)通常是指通过设计保证系统能够同时并行处理很多请求。通俗来讲,高并发是指在同一个时间点,有很多用户同时的访问同一 API 接口或者 Url 地址。它经常会发生在有大活跃用户量,用户高聚集的业务场景中。

简单分析秒杀

所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式。

  • 那么秒杀商品通常有两种限制:时间限制库存限制

数据库设计

  • 由我们所了解的限时秒杀业务流程可知,先要有秒杀商品表,并且不是提交到原来的普通订单表上,要设计一张秒杀订单表,故设计一个秒杀业务至少需要两张表
  1. 商品库存表:stock表
    stock表字段设计
  2. 秒杀订单表:t_order表
    使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第1张图片

限时秒杀实现前提

1. 安装RabbitMQ
这个我之前的博客里面有详细操作的链接跟着来就完事向下
安装RabbitMQ详细步骤

  • 安装完成后展示页面
    使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第2张图片

2. Redis+RedisDesktopManager

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

上面只是简单了解一下Redis的定义,下面为具体安装步骤的链接,点进去跟着操作
Redis 以及 可视化工具Redis Desktop Manager的安装和使用

  • 安装完成后展示页面
    使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第3张图片
    3. Jmeter压力测试工具

Jmeter为基于java得压力测试工具,在此项目demo中用它模仿40个不同得用户同时发送请求秒杀商品

下面是Jmeter的安装教程,跟着操作就完了
Jmeter的安装教程

  • 安装完成后展示页面
    使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第4张图片

项目搭建

1.新建Springboot项目
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第5张图片
2. 初始化完成后,pom.xml导入需要的jar包(以下是所有需要的jar包)

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.3-beta1</version>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.0.0</version>
        </dependency>
    </dependencies>
  1. 配置application.properties
##配置数据库连接
server.port=8443
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/myredis?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
##指定hibernate.dialect
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
##配置rabbitmq连接
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
##配置连接redis --都记得打开服务
spring.redis.host=localhost
spring.redis.port=6379 
spring.redis.jedis.pool.max-active=1024
spring.redis.jedis.pool.max-wait=-1s
spring.redis.jedis.pool.max-idle=200
spring.redis.password=123456

这个时候可以启动项目看看有没有报错,测试一下配置有没有问题

  1. pojo包下新加实体类
Order.java
package com.redis.pojo;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;


/**
 * @author 笑笑小太阳
 * @date 2019/12/24 14:51
 */
@Data
@Table(name = "t_order")

public class Order implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "order_name")
    private String order_name;

    @Column(name = "order_user")
    private String order_user;
}
Stock.java
package com.redis.pojo;

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;


/**
 * @author 笑笑小太阳
 * @date 2019/12/24 14:51
 */
@Table(name = "stock")
@Data
public class Stock implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "stock")
    private Long stock;
}

因为本次数据库操作方面使用了tkmybatis框架,所以实体类我们需要用到JPA的注解,来实现映射关系!!

  1. 配置tkmybatis的接口
  • 新增一个base的包,与pojo同级,在base下面新建service接口,如下图
    在这里插入图片描述
GenericMapper.interface
package com.redis.base;

import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 14:58
 */
public interface GenericMapper<T> extends Mapper<T>, MySqlMapper<T> {
}

tkmybatis你不需要了解太多,你只要知道我们的mapper层需要通过继承它来实现数据库操作,方法跟jpa和mybatis-plus相似

  1. 新建mapper层
    在这里插入图片描述
OrderMapper.interface
package com.redis.mapper;

import com.redis.base.GenericMapper;
import com.redis.pojo.Order;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 14:56
 */
@Mapper
public interface OrderMapper extends GenericMapper<Order> {
    void insertOrder(Order order);
}

StockMapper.interface
package com.redis.mapper;

import com.redis.base.GenericMapper;
import com.redis.pojo.Stock;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 14:57
 */

@Mapper
public interface StockMapper extends GenericMapper<Stock> {
}

  1. 编写RabbitMQ和redis的配置类
    在这里插入图片描述
MyRabbitMQConfig.java
package com.redis.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;



/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:01
 */
@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();
    }
}

RedisConfig .java
package com.redis.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;



/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:04
 */
@Configuration
public class RedisConfig {
    @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;
    }
}

  1. 编写service层(新建service包以及impl包,这里只提供实现类,接口可以自行编写)
OrderServiceImpl .java
package com.redis.service.impl;

import com.redis.mapper.OrderMapper;
import com.redis.pojo.Order;
import com.redis.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:07
 */
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderMapper orderMapper;

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

StockServiceImpl.java
package com.redis.service.impl;


import com.redis.mapper.StockMapper;
import com.redis.pojo.Stock;
import com.redis.service.StockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;
import java.util.List;


/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:10
 */
@Service
public class StockServiceImpl implements StockService {
    @Autowired
    private StockMapper stockMapper;

    @Override
    public void decrByStock(String stockName) {
        Example example = new Example(Stock.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("name", stockName);
        List<Stock> stocks = stockMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(stocks)) {
            Stock stock = stocks.get(0);
            stock.setStock(stock.getStock() - 1);
            stockMapper.updateByPrimaryKey(stock);
        }
    }

    @Override
    public Integer selectByExample(String stockName) {
        Example example = new Example(Stock.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("name", stockName);
        List<Stock> stocks = stockMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(stocks)) {
            return stocks.get(0).getStock().intValue();
        }
        return 0;
    }
}

  1. 配置rabbitmq的实现方式以及redis的实现方式
  • 在 service包下面直接新建 MQOrderService.java
    这个类属于订单的消费队列
package com.redis.service;

import com.redis.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;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:15
 */
@Service
@Slf4j
public class MQStockService {
    @Autowired
    private StockService stockService;
    /**
     * 监听库存消息队列,并消费
     * @param stockName
     */
    @RabbitListener(queues = MyRabbitMQConfig.STORY_QUEUE)
    public void decrByStock(String stockName) {
        log.info("库存消息队列收到的消息商品信息是:{}", stockName);
        /**
         * 调用数据库service给数据库对应商品库存减一
         */
        stockService.decrByStock(stockName);
    }
}

MQStockService.java

这个属于库存得消费队列

package com.redis.service;

import com.redis.config.MyRabbitMQConfig;
import com.redis.pojo.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;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:14
 */
@Service
@Slf4j
public class MQOrderService {
    @Autowired
    private OrderService orderService;
    /**
     * 监听订单消息队列,并消费
     *
     * @param order
     */
    @RabbitListener(queues = MyRabbitMQConfig.ORDER_QUEUE)
    public void createOrder(Order order) {
        log.info("收到订单消息,订单用户为:{},商品名称为:{}", order.getOrder_user(), order.getOrder_name());
        /**
         * 调用数据库orderService创建订单信息
         */
        orderService.createOrder(order);
    }
}

RedisService.java
  • 这个配置类,主要用来实现对redis得key和value初始化以及对value得操作
package com.redis.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;


/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:16
 */
@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 设置String键值对
     * @param key
     * @param value
     * @param millis
     */
    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);
    }

    /**
     * 对指定key的键值减一
     * @param key
     * @return
     */
    public Long decrBy(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }
}


下面为service包的完整目录
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第6张图片
10. controller层
该controller提供了二个方法,一个为redis+rabbitmq实现高并发秒杀,第二个则用纯数据库模拟秒杀,出现超卖现象

package com.redis.controller;


import com.redis.config.MyRabbitMQConfig;
import com.redis.pojo.Order;
import com.redis.service.OrderService;
import com.redis.service.RedisService;
import com.redis.service.StockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author 笑笑小太阳
 * @date 2019/12/24 15:28
 */
@Controller
@Slf4j
public class SecController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisService redisService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private StockService stockService;

    /**
     * 使用redis+消息队列进行秒杀实现
     *
     * @param username
     * @param stockName
     * @return
     */
    @PostMapping( value = "/sec",produces = "application/json;charset=utf-8")
    @ResponseBody
    public String sec(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {

        log.info("参加秒杀的用户是:{},秒杀的商品是:{}", username, stockName);
        String message = null;
        //调用redis给相应商品库存量减一
        Long decrByResult = redisService.decrBy(stockName);
        if (decrByResult >= 0) {
            /**
             * 说明该商品的库存量有剩余,可以进行下订单操作
             */
            log.info("用户:{}秒杀该商品:{}库存有余,可以进行下订单操作", username, stockName);
            //发消息给库存消息队列,将库存数据减一
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.STORY_EXCHANGE, MyRabbitMQConfig.STORY_ROUTING_KEY, stockName);

            //发消息给订单消息队列,创建订单
            Order order = new Order();
            order.setOrder_name(stockName);
            order.setOrder_user(username);
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE, MyRabbitMQConfig.ORDER_ROUTING_KEY, order);
            message = "用户" + username + "秒杀" + stockName + "成功";
        } else {
            /**
             * 说明该商品的库存量没有剩余,直接返回秒杀失败的消息给用户
             */
            log.info("用户:{}秒杀时商品的库存量没有剩余,秒杀结束", username);
            message = "用户:"+ username + "商品的库存量没有剩余,秒杀结束";
        }
        return message;
    }

    /**
     * 实现纯数据库操作实现秒杀操作
     * @param username
     * @param stockName
     * @return
     */
    @RequestMapping("/secDataBase")
    @ResponseBody
    public String secDataBase(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {
        log.info("参加秒杀的用户是:{},秒杀的商品是:{}", username, stockName);
        String message = null;
        //查找该商品库存
        Integer stockCount = stockService.selectByExample(stockName);
        log.info("用户:{}参加秒杀,当前商品库存量是:{}", username, stockCount);
        if (stockCount > 0) {
            /**
             * 还有库存,可以进行继续秒杀,库存减一,下订单
             */
            //1、库存减一
            stockService.decrByStock(stockName);
            //2、下订单
            Order order = new Order();
            order.setOrder_user(username);
            order.setOrder_name(stockName);
            orderService.createOrder(order);
            log.info("用户:{}.参加秒杀结果是:成功", username);
            message = username + "参加秒杀结果是:成功";
        } else {
            log.info("用户:{}.参加秒杀结果是:秒杀已经结束", username);
            message = username + "参加秒杀活动结果是:秒杀已经结束";
        }
        return message;
    }

}


  1. 编写springboot启动类
    需要在springboot得启动类中进行对redis得初始化,简而言之就是调用我们上面写得方法,新建一个redis缓存,模拟商品信息
package com.redis;

import com.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import tk.mybatis.spring.annotation.MapperScan;


@SpringBootApplication
@MapperScan("com.redis.mapper")
public class RedisApplication implements ApplicationRunner {

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

    @Autowired
    private RedisService redisService;

    /**
     * redis初始化商品的库存量和信息
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        redisService.put("watch", 10, 20);
    }

}

完整项目层次浏览
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第7张图片

测试

redis+rabbitmq
前提:启动之前记得打开redis和rabbitmq的服务

  1. 启动成功之后打开Redis Desktop Manager工具,查看是否新建了一个redis :watch

使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第8张图片

  1. 打开我们得JMeter工具运行测试(具体使用Jmeter可自行百度)

首先选择中文
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第9张图片
完成中文之后,我们在测试计划右键,添加一个线程组
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第10张图片
给这个线程组的数量为40,这个线程组的作用就是模拟40个用户发送请求,去秒杀.
然后再在线程组右键,添加一个Http请求,这个就是我们用来发送请求的组件了使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第11张图片
在这里插入图片描述
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第12张图片
这个请求唯一要说得就是,随机参数了,因为用户名肯定不可能给40个相同得名字,这边我们利用JMeter给用户名得值为随机数
点击上方得白色小书本,选择random,1-99得随机数
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第13张图片
然后我们把这个函数字符串复制到http得参数上面去
在这里插入图片描述

最后我们在测试计划建一个结果树,查看我们发送请求返回得消息数据在这里插入图片描述
运行
在这里插入图片描述
4. 运行成功可以发现控制台是这样的
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第14张图片
可以看到运行结果已经打印到控制台了,用户名为我们生成的随机数。
再来看下数据库订单表t_order
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第15张图片
图中有10条秒杀到商品得用户信息和商品名,我再帮大家理一理,我们初始化的时候给watch库存得数量为10,而我们使用JMeter模拟了40个人发请求,所以这10条数据,也就是40个用户中抢到商品的10个人,也就是线程,谁抢到就是谁得。
再来查看下我们得结果树
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第16张图片
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第17张图片

纯数据库方式秒杀结果
上面我们实现了redis+rabbitmq得秒杀,现在我们看看纯数据库方式得秒杀,看看有什么区别:

首先网stock库存表新增一条数据,类似于redis得初始化
在这里插入图片描述
在jmeter中修改原来得http请求信息,其中小米对应数据库得商品名
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第18张图片
在这里插入图片描述
清空一下结果树,我们开始运行
run
控制台:
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第19张图片
重要得是查看数据库得信息:
在这里插入图片描述
库存已经清空,再看order表
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第20张图片
这样我们可以看到,明明只有10个库存得商品,抢到得人却不止10个,这样明细超卖了,请求树也可以看的超卖信息
使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀_第21张图片

如有不足,请指正,借鉴了某刘姓大佬的博客,如有不会的,我帮你问他

你可能感兴趣的:(redis,mysql,java,intellij,idea)