主頁 >  其他 > 實戰深入了解redis+訊息佇列如何實作秒殺

實戰深入了解redis+訊息佇列如何實作秒殺

2021-09-14 11:50:28 其他

SpringBoot +Redis +RabbitMQ 實作高并發限時秒殺

所謂秒殺,從業務角度看,是短時間內多個用戶“爭搶”資源,這里的資源在大部分秒殺場景里是商品;將業務抽象,技術角度看,秒殺就是多個執行緒對資源進行操作,所以實作秒殺,就必須控制執行緒對資源的爭搶,既要保證高效并發,也要保證操作的正確,

文章目錄

    • SpringBoot +Redis +RabbitMQ 實作高并發限時秒殺
      • 開發環境
      • 測驗環境
      • 環境安裝
        • 安裝RabbitMQ
        • 安裝Redis
        • 安裝 Jmeter測驗工具
        • maven安裝
      • 代碼模塊
        • 資料庫設計
          • 商品庫存表:stock
          • 秒殺訂單表:t_order
        • pom.xml
        • application.yml
        • logback-spring.xml
        • 物體類
        • 服務層
        • 介面實作類
        • 配置rabbitmq的實作方式以及redis的實作方式
        • 工具類
      • 控制層
      • 啟動專案
        • 啟動完成,redis中上架10個貨物,
        • 打開JMeter并發環境測驗
        • log日志

開發環境

SpringBoot+mysql+maven+JDK8+RabbitMQ+Redis

測驗環境

Jmeter測驗工具

環境安裝

安裝RabbitMQ

docker安裝:https://blog.csdn.net/qq_33612228/article/details/103732890
windows安裝:https://blog.csdn.net/m0_37034294/article/details/82839494

需要安裝mq的可視化工具
在這里插入圖片描述

安裝Redis

docker安裝:https://blog.csdn.net/qq_33612228/article/details/10360918
windows安裝:https://blog.csdn.net/qq_39135287/article/details/82686837
springboot整合redis:https://blog.csdn.net/qq_33612228/article/details/103700543

redis需要安裝客戶端,方便資料查看,
在這里插入圖片描述

安裝 Jmeter測驗工具

windows安裝:https://blog.csdn.net/liuyanh2006/article/details/82494548
mac安裝: https://blog.csdn.net/zgy_boke/article/details/102689531
mac安裝需要的Homebrew:https://blog.csdn.net/o_o814222198/article/details/120185851?spm=1001.2014.3001.5502

maven安裝

mac安裝:https://www.jianshu.com/p/191685a33786
window安裝:https://www.cnblogs.com/eagle6688/p/7838224.html

代碼模塊

資料庫設計

商品庫存表:stock
CREATE TABLE `stock` (
  `id` varchar(64) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `stock` varchar(255) DEFAULT NULL,
  `remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '備注',
  `update_date` datetime DEFAULT NULL COMMENT '最后更新時間',
  `create_date` datetime DEFAULT NULL COMMENT '創建時間',
  `update_by` varchar(64) NOT NULL DEFAULT '',
  `create_by` varchar(64) NOT NULL DEFAULT '',
  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0正常,1洗掉',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='商品庫存表';
秒殺訂單表:t_order
CREATE TABLE `t_order` (
  `id` varchar(64) NOT NULL,
  `order_name` varchar(255) DEFAULT NULL,
  `order_user` varchar(255) DEFAULT NULL,
  `remarks` varchar(255) NOT NULL DEFAULT '' COMMENT '備注',
  `update_date` datetime DEFAULT NULL COMMENT '最后更新時間',
  `create_date` datetime DEFAULT NULL COMMENT '創建時間',
  `update_by` varchar(64) NOT NULL DEFAULT '',
  `create_by` varchar(64) NOT NULL DEFAULT '',
  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0正常,1洗掉',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='秒殺訂單表';

pom.xml

  1. 集成MybatisPlus
  2. fastjosn依賴
  3. Druid
  4. MyBatis增強工具
  5. lombok
  6. websocket
  7. 前端頁面通過thymeleaf渲染
  8. 工具類包
  9. swagger-ui
  10. redis
  11. RabbitMQ
<?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.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>集成MybatisPlus</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <druid.version>1.1.14</druid.version>
        <mysql-connector-java.version>8.0.11</mysql-connector-java.version>
        <mybatis-plus-boot-starter.version>3.1.1</mybatis-plus-boot-starter.version>
        <mybatis-plus-generator.version>3.1.1</mybatis-plus-generator.version>
        <lombok.version>1.18.0</lombok.version>
        <swagger.version>2.9.2</swagger.version>
        <fastjson.version>1.2.47</fastjson.version>
        <commons-text.version>1.6</commons-text.version>
    </properties>

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

        <!--依賴管理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.8.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--mysql依賴-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector-java.version}</version>
        </dependency>
        <!--阿里巴巴fastjosn依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- Druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!-- MyBatis增強工具-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus-generator.version}</version>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <!-- websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <!-- 前端頁面通過thymeleaf渲染 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--工具類包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>${commons-text.version}</version>
        </dependency>

        <!--swagger-ui-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>

        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

  1. 資料源
  2. 埠號
  3. redis
  4. mq
  5. 掃描mapper檔案
  6. 檔案上傳
  7. 頁面模板
  8. log日志配置
spring:
  datasource:
    url: jdbc:mysql://182.92.210.212:3306/order_db?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: ***
    # 使用Druid資料源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
  data:
    redis:
      repositories:
        enabled: false
  redis:
    database: 0   # redis資料庫索引(默認為0),我們使用索引為其他(0-15)的資料庫,避免和其他資料庫沖突
    host: 182.92.210.212
    port: 6379
    password: ***
  rabbitmq:  #mq配置
    host: localhost
    port: 5672
    username: guest
    password: guest
  resources:
    static-locations: classpath:/static #這個配置項告訴springboot去哪找資源 最關鍵的地方
  profiles:
    active: dev
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB #允許上傳的檔案大小
      max-request-size: 10MB
  thymeleaf:
    cache: false
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    mode: HTML5
    context-type: text/html
  mvc:
    view:
      prefix: /
      suffix: .html
    static-path-pattern: /**  #這個配置告訴springboot,應該以什么方式去找資源,默認是/*
mybatis-plus:
  mapper-locations: classpath*:com.example.demo.mapper/*.xml
  global-config:
    db-config:
      id-type: uuid
      field-strategy: not_null
    refresh: true
  configuration:
    map-underscore-to-camel-case: false
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

server:
  port: 8090
logging:
  config: classpath:logback-spring.xml

logback-spring.xml

  1. 各級別日志分開列印,一個等級一個日志檔案,
  2. 每天存檔,
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>logback</contextName>
    <property name="path" value="./log"/>
    <property name="maxHistory" value="30"/>
    <property name="maxFileSize" value="50MB"/>
    <!--輸出sql陳述句-->
    <logger name="com.example.demo.mapper" level="debug"/>
    <!--輸出到控制臺-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
             <level>ERROR</level>
         </filter>-->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--debug_file-->
    <appender name="debug_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_debug.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!--info_file-->
    <appender name="info_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_info.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!--warn_file-->
    <appender name="warn_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_warn.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--error_file-->
    <appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一歸檔 -->
            <fileNamePattern>${path}/logback_error.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="debug_file"/>
        <appender-ref ref="info_file"/>
        <appender-ref ref="warn_file"/>
        <appender-ref ref="error_file"/>
    </root>
</configuration>

物體類

package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author dispark
 * @version 1.0
 * @Description: 訂單
 * @date 2019-12-27 15:54
 */
@TableName("t_order")
@Data
public class Order extends BasePlusEntity<Order>{
    private static final long serialVersionUID = 1L;

    /**
     * 訂單名稱
     */
    @TableField("order_name")
    private String orderName;


    /**
     * 訂單用戶
     */
    @TableField("order_user")
    private String orderUser;

}

package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author dispark
 * @version 1.0
 * @Description: 存貨
 * @date 2019-12-27 15:54
 */
@TableName("stock")
@Data
public class Stock extends BasePlusEntity<Stock>{
    private static final long serialVersionUID = 1L;

    /**
     * 產品名稱
     */
    @TableField("name")
    private String name;


    /**
     * 存貨
     */
    @TableField("stock")
    private String stock;

}

服務層

package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Order;

/**
 * @author dispark
 * @version 1.0
 * @Description: 訂單服務層
 * @date 2019-12-27 15:54
 */
public interface OrderService extends IService<Order> {
    /**
     * 訂單保存
     * @param order 物體
     */
    void saveOrder(Order order);
}
package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Stock;

/**
 * @author dispark
 * @version 1.0
 * @Description: 存貨服務層
 * @date 2019-12-27 15:54
 */
public interface StockService extends IService<Stock> {
    /**
     * 秒殺商品后-減少庫存
     * @param name 商品名稱
     */
    void decrByStock(String name);

    /**
     * 秒殺商品前判斷是否有庫存
     * @param name 商品名稱
     * @return
     */
    Integer selectByName(String name);

    /**
     * 實作純資料庫操作實作秒殺操作
     * @param userName 用戶名稱
     * @param stockName 商品名稱
     * @return String
     */
    String secDataBase(String userName,String stockName);
}

介面實作類

package com.example.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.Order;
import com.example.demo.exception.SXException;
import com.example.demo.exception.ServiceExceptionEnum;
import com.example.demo.mapper.OrderMapper;
import com.example.demo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author dispark
 * @version 1.0
 * @Description: 訂單實作層
 * @date 2019-12-27 15:54
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 訂單保存
     * @param order 物體
     */
    @Override
    public void saveOrder(Order order) {
        if(orderMapper.insert(order) <= 0){
            throw new SXException(ServiceExceptionEnum.DATA_INSERT_EXCEPTION);
        }
    }
}
package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.Order;
import com.example.demo.entity.Stock;
import com.example.demo.mapper.StockMapper;
import com.example.demo.service.OrderService;
import com.example.demo.service.StockService;
import com.example.demo.utils.IdGenerate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * @author dispark
 * @version 1.0
 * @Description: 存貨實作層
 * @date 2019-12-27 15:54
 */
@Service
@Slf4j
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements StockService {

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private OrderService orderService;

    /**
     * 秒殺商品后-減少庫存
     * @param name 商品名稱
     */
    @Override
    public void decrByStock(String name) {
        List<Stock> stockList = stockMapper.selectList(new QueryWrapper<Stock>().lambda().eq(Stock::getName, name));
        stockList.forEach(stock -> {
            //貨物購買
            stock.setStock(String.valueOf(Integer.parseInt(stock.getStock())-1));
            stockMapper.updateById(stock);
        });
    }

    /**
     * 秒殺商品前判斷是否有庫存
     * @param name 商品名稱
     * @return
     */
    @Override
    public Integer selectByName(String name) {
        //查詢存貨數量
        Integer stockNum = 0;
        List<Stock> stockList = stockMapper.selectList(new QueryWrapper<Stock>().lambda().eq(Stock::getName, name));
        if(stockList.size() > 0){
            stockNum = Integer.parseInt(stockList.get(0).getStock());
        }
        return stockNum;
    }

    /**
     * 實作純資料庫操作實作秒殺操作
     * @param userName 用戶名稱
     * @param stockName 商品名稱
     * @return String
     */
    @Override
    public String secDataBase(String userName, String stockName) {
        log.info("參加秒殺的用戶是:{},秒殺的商品是:{}", userName, stockName);
        String message = null;
        //查找該商品庫存
        Integer stockCount = selectByName(stockName);
        log.info("用戶:{}參加秒殺,當前商品庫存量是:{}", userName, stockCount);
        if (stockCount > 0) {
            /**
             * 還有庫存,可以進行繼續秒殺,庫存減一,下訂單
             */
            //1、庫存減一
            decrByStock(stockName);
            //2、下訂單
            Order order = new Order();
            order.setOrderUser(userName);
            order.setOrderName(stockName);
            order.setCreateBy(userName);
            order.setCreateDate(new Date());
            order.setUpdateBy(userName);
            order.setUpdateDate(new Date());
            order.setDelFlag("0");
            order.setId(IdGenerate.generateId());
            orderService.saveOrder(order);
            log.info("用戶:{}.參加秒殺結果是:成功", userName);
            message = userName + "參加秒殺結果是:成功";
        } else {
            log.info("用戶:{}.參加秒殺結果是:秒殺已經結束", userName);
            message = userName + "參加秒殺活動結果是:秒殺已經結束";
        }
        return message;
    }
}

配置rabbitmq的實作方式以及redis的實作方式

  1. 訂單的消費佇列
package com.example.demo.service.impl;

import com.example.demo.config.RabbitMqConfig;
import com.example.demo.entity.Order;
import com.example.demo.service.OrderService;
import com.example.demo.utils.IdGenerate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @author dispark
 * @version 1.0
 * @Description: MQ訂單實作層
 * @date 2019-12-27 15:54
 */
@Service
@Slf4j
public class MQOrderServiceImpl {

    @Autowired
    private OrderService orderService;

    /**
     * MQ監聽訂單訊息佇列,并消費
     * @param order
     */
    @RabbitListener(queues = RabbitMqConfig.ORDER_QUEUE)
    public void saveOrder(Order order) {
        log.info("收到訂單訊息,訂單用戶為:{},商品名稱為:{}", order.getOrderUser(), order.getOrderName());
        /**
         * 呼叫資料庫orderService創建訂單資訊
         */
        order.setCreateBy(order.getOrderUser());
        order.setCreateDate(new Date());
        order.setUpdateBy(order.getOrderUser());
        order.setUpdateDate(new Date());
        order.setDelFlag("0");
        order.setId(IdGenerate.generateId());
        orderService.saveOrder(order);
    }
}
  1. 庫存得消費佇列
package com.example.demo.service.impl;

import com.example.demo.config.RabbitMqConfig;
import com.example.demo.entity.Order;
import com.example.demo.service.StockService;
import com.example.demo.utils.IdGenerate;
import com.example.demo.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @author dispark
 * @version 1.0
 * @Description: MQ存貨實作層
 * @date 2019-12-27 15:54
 */
@Service
@Slf4j
public class MQStockServiceImpl {

    @Autowired
    private StockService stockService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisUtil redisUtil;


    /**
     * 秒殺商品后-減少庫存
     *
     * @param name 商品名稱
     * @author dispark
     * @date 2021/9/10 9:51 上午
     */
    @RabbitListener(queues = RabbitMqConfig.STORY_QUEUE)
    public void decrByStock(String name) {
        log.info("庫存訊息佇列收到的訊息商品資訊是:{}", name);
        // 呼叫資料庫service給資料庫對應商品庫存減一
        stockService.decrByStock(name);
    }

    /**
     * 使用redis+訊息佇列進行秒殺實作
     *
     * @param userName  用戶
     * @param stockName 商品
     * @return {@link java.lang.String}
     * @author dispark
     * @date 2021/9/10 9:51 上午
     */
    public String secKill(String userName, String stockName) {
        log.info("參加秒殺的用戶是:{},秒殺的商品是:{}", userName, stockName);
        String message = "";
        // 1.呼叫redis給相應商品庫存量減1
        Long decrByResult = redisUtil.decrBy(stockName);
        if (decrByResult >= 0) {
            // 2.說明該商品的庫存量有剩余,可以進行下訂單操作
            log.info("用戶:{}秒殺該商品:{}庫存有余,可以進行下訂單操作", userName, stockName);
            // 3.發訊息給庫存訊息佇列,將庫存資料減一
            rabbitTemplate.convertAndSend(RabbitMqConfig.STORY_EXCHANGE, RabbitMqConfig.STORY_ROUTING_KEY, stockName);

            // 4.發訊息給訂單訊息佇列,創建訂單
            Order order = new Order();
            order.setOrderName(stockName);
            order.setOrderUser(userName);
            rabbitTemplate.convertAndSend(RabbitMqConfig.ORDER_EXCHANGE, RabbitMqConfig.ORDER_ROUTING_KEY, order);
            message = "用戶" + userName + "秒殺" + stockName + "成功";
        } else {
            // 5.說明該商品的庫存量沒有剩余,直接回傳秒殺失敗的訊息給用戶
            log.info("用戶:{}秒殺時商品的庫存量沒有剩余,秒殺結束", userName);
            message = "用戶:" + userName + "商品的庫存量沒有剩余,秒殺結束";
        }
        return message;
    }

}

工具類

RabbitMQConfig插件配置

package com.example.demo.config;

import org.springframework.amqp.core.*;
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 dispark
 * @version 1.0
 * @Description: RabbitMQConfig插件配置
 * @date 2019-12-27 16:23
 */
@Configuration
public class RabbitMqConfig {
    /**
     * 庫存交換機
     */
    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();
    }

    /**
     * 創建庫存交換機
     *
     * @return
     */
    @Bean
    public Exchange getStoryExchange() {
        return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();
    }

    /**
     * 創建庫存佇列
     *
     * @return
     */
    @Bean
    public Queue getStoryQueue() {
        return new Queue(STORY_QUEUE, true);
    }

    /**
     * 庫存交換機和庫存佇列系結
     *
     * @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();
    }

}

redis 快取配置

package com.example.demo.config;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @ClassName: RedisCacheConfig
 * @Description: redis 快取配置;
 * 注意:RedisCacheConfig這里也可以不用繼承:CachingConfigurerSupport,
 * 也就是直接一個普通的Class就好了 這里主要我們之后要重新實作
 * key的生成策略,只要這里修改KeyGenerator,其它位置不用修改就生效了,
 * 普通使用普通類的方式的話,那么在使用@Cacheable的時候還需要指定KeyGenerator的名稱;
 * 這樣編碼的時候比較麻煩,
 * @author: dispark
 * @date: 2019年12月25日 下午3:30:19
 */
@Configuration
@EnableCaching // 啟用快取,這個注解很重要;
public class RedisCacheConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    /**
     * 快取配置初始化一個cacheManager
     * @param connectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).build();
        return redisCacheManager;
    }


    /**
     * 防止redis入庫序列化亂碼的問題
     * @param redisConnectionFactory
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用Jackson2JsonRedisSerialize 替換默認序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 設定value的序列化規則和 key的序列化規則
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 重寫hashOperations
     * @param redisTemplate
     * @return
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    /**
     * 重寫listOperations
     * @param redisTemplate
     * @return
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    /**
     * redisMessageListenerContainer
     * @return
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        return redisMessageListenerContainer;
    }
}

package com.example.demo.utils;


import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;


/**
 * @author LST
 * @version 1.0
 * @Description: redis工具類
 * @date 2019-12-25 16:52
 */
@Component
public class RedisUtil<T> {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private HashOperations<String, String, Object> hashOperations;

    @Autowired
    private ListOperations<String, Object> listOperations;

    /**
     * 默認過期時長,單位:秒/ 三十分鐘
     */
    public final static long DEFAULT_EXPIRE = 180 * 10;

    /**
     * 不設定過期時長
     */
    public final static long NOT_EXPIRE = -1;

    /**
     * 普通快取放入
     *
     * @param key   鍵
     * @param value 值
     */
    public void setValue(String key, T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 普通快取放入并設定時間
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒) time要大于0 如果time小于等于0 將設定無限期
     */
    public void setValue(String key, T value, Long time) {
        setValue(key, value);
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 普通快取放入并設定時間和單位
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒) time要大于0 如果time小于等于0 將設定無限期
     * @param unit  快取設定的時間單位
     */
    public void setValue(String key, T value, Long time, TimeUnit unit) {
        setValue(key, value);
        redisTemplate.expire(key, time, unit);
    }

    /**
     * 普通快取獲取
     *
     * @param key 鍵
     * @return 值
     */
    public T getValue(String key) {
        ValueOperations<String, T> valueOperations = redisTemplate.opsForValue();
        return valueOperations.get(key);
    }

    /**
     * 洗掉快取
     *
     * @param key 鍵
     */
    public void deleteValue(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 判斷key是否存在
     *
     * @param key 鍵
     * @return true 存在  false 不存在
     */
    public boolean exists(String key) {
        if (getValue(key) == null) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 放入一個map物件
     *
     * @param key 鍵
     * @param map 物件
     * @return true 存在  false 不存在
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * HashSet 并設定時間
     *
     * @param key  鍵
     * @param map  對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Map快取獲取
     *
     * @param key 鍵
     * @return map
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 重復提交
     *
     * @param key 鍵
     * @return
     */
    public long repeatSubmit(String key) {
        ValueOperations<String, Integer> valueOperations = redisTemplate.opsForValue();
        long ret = valueOperations.increment(key, 1);
        redisTemplate.expire(key, 10L, TimeUnit.SECONDS);
        return ret;
    }

    /**
     * 將list放入快取
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 獲取list快取的內容
     *
     * @param key   鍵
     * @param start 開始
     * @param end   結束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 洗掉hash值
     *
     * @param key      key
     * @param hashKeys hashKeys
     */
    public void delHash(String key, String... hashKeys) {
        Arrays.stream(hashKeys).forEach(hashKey -> hashOperations.delete(key, hashKeys));
    }


    /**
     * 先根據key洗掉
     * 然后在把list放入快取中
     *
     * @param key                key
     * @param getHashKeyFunction 獲取hashKey值的方法
     * @param list               list物體類
     * @param expire             時間
     */
    public <T> void putHashList(String key, Function<T, String> getHashKeyFunction, List<T> list, Long expire) {
        putHashList(key, getHashKeyFunction, list);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    /**
     * 先根據key洗掉
     * 然后在把list放入快取中
     *
     * @param key                鍵
     * @param getHashKeyFunction
     * @param list
     * @param <T>
     */
    public <T> void putHashList(String key, Function<T, String> getHashKeyFunction, List<T> list) {
        deleteValue(key);
        list.stream().filter(distinctByValue(getHashKeyFunction)).forEach(model -> putHashModel(key, getHashKeyFunction, model));
    }


    /**
     * 將物體類放到Hash里面,并設定時間
     *
     * @param key                key
     * @param getHashKeyFunction 獲取hashKey值的方法
     * @param model              物體類
     * @param <T>                T
     */
    public <T> void putHashModel(String key, Function<T, String> getHashKeyFunction, T model, Long expire) {
        putHashModel(key, getHashKeyFunction, model);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    /**
     * 將物體類放到Hash里面
     *
     * @param key                key
     * @param getHashKeyFunction 獲取hashKey值的方法
     * @param model              物體類
     * @param <T>                T
     */
    public <T> void putHashModel(String key, Function<T, String> getHashKeyFunction, T model) {
        hashOperations.put(key, getHashKeyFunction.apply(model), JSONObject.toJSON(model).toString());
    }


    /**
     * 根據有效時間,key 獲取List
     *
     * @param key   key
     * @param clazz clazz
     * @param <T>   T
     * @return List<T>
     */
    public <T> List<T> getHashList(String key, Class<T> clazz, Long expire) {
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return getHashList(key, clazz);
    }

    /**
     * 獲取全部list
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> getHashList(String key, Class<T> clazz) {
        Optional<List<Object>> objectList = Optional.ofNullable(hashOperations.values(key));
        return objectList.map(objects -> objects.stream()
                .map(t -> JSONObject.parseObject(JSONObject.toJSON(t).toString(), clazz))
                .collect(Collectors.toList())).orElse(null);
    }


    /**
     * 根據 key 和 hashKey 獲取值
     *
     * @param key     key
     * @param hashKey hashKey
     * @param clazz   clazz
     * @param <T>     T
     * @return T
     */
    public <T> T getHashObject(String key, String hashKey, Class<T> clazz) {
        Optional<Object> object = Optional.ofNullable(hashOperations.get(key, hashKey));
        if (object.isPresent()) {
            return JSONObject.parseObject(JSONObject.toJSON(object).toString(), clazz);
        }
        return null;
    }

    /**
     * 去除重復
     *
     * @param keyExtractor
     * @param <T>
     * @return
     */
    public static <T> Predicate<T> distinctByValue(Function<? super T, ?> keyExtractor) {
        ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }


    /**
     * 判斷key是否存在
     *
     * @param key 鍵
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 獲取有效全部list
     *
     * @param key    key
     * @param clazz  clazz
     * @param <T>    T
     * @param expire 超時時間
     * @return return
     */
    public <T> List<T> getList(String key, Class<T> clazz, Long expire) {
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return getList(key, clazz);
    }

    /**
     * 獲取全部list
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> getList(String key, Class<T> clazz) {
        Optional<List<Object>> list = Optional.ofNullable(listOperations.range(key, 0, listOperations.size(key)));
        return list.map(objects -> objects.stream()
                .map(t -> JSONObject.parseObject(JSONObject.toJSON(t).toString(), clazz))
                .collect(Collectors.toList())).orElse(null);
    }

    /**
     * 先根據key洗掉
     * 然后在把list放入快取中
     *
     * @param key              鍵
     * @param getValueFunction 獲取value值的方法
     * @param list             list
     * @param <T>              T
     * @param expire           超時時間
     */
    public <T> void leftPushList(String key, List<T> list, Function<T, String> getValueFunction, Long expire) {
        leftPushList(key, list, getValueFunction);
        if (expire != NOT_EXPIRE) {
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    /**
     * 物體類去重放入listOperations
     *
     * @param key
     * @param list
     * @param getValueFunction 獲取value值的方法
     * @param <T>
     */
    public <T> void leftPushList(String key, List<T> list, Function<T, String> getValueFunction) {
        deleteValue(key);
        list.stream().filter(distinctByValue(getValueFunction)).forEach(model -> leftPushModel(key, model, getValueFunction));
    }

    /**
     * 物體類放入
     *
     * @param key              key
     * @param model            model
     * @param getValueFunction 獲取value值的方法
     * @return <T>
     * @author dispark
     * @date 2021/9/10 9:54 上午
     */
    public <T> void leftPushModel(String key, T model, Function<T, String> getValueFunction) {
        listOperations.leftPush(key, getValueFunction.apply(model));
    }

    /**
     * 對指定key的鍵值減1
     *
     * @param key key
     * @return {@link java.lang.Long}
     * @author dispark
     * @date 2021/9/10 9:53 上午
     */
    public Long decrBy(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }
}

控制層

redis+訊息佇列進行秒殺實作

package com.example.demo.controller;

import com.example.demo.result.RestResponse;
import com.example.demo.result.ResultGenerator;
import com.example.demo.service.StockService;
import com.example.demo.service.impl.MQStockServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author dispark
 * @version 1.0
 * @Description: 秒殺
 * @date 2019-12-28 20:59
 */
@RestController
@Api(value = "SecKillController", tags = "秒殺控制層")
@Slf4j
public class SecKillController {


    @Autowired
    private MQStockServiceImpl mqStockService;

    @Autowired
    private StockService stockService;


    /**
     * 使用redis+訊息佇列進行秒殺實作
     *
     * @param userName  用戶名稱
     * @param stockName 商品名稱
     * @return {@link com.example.demo.result.RestResponse}
     * @author dispark
     * @date 2021/9/10 9:47 上午
     */
    @PostMapping(value = "sec-kill", produces = "application/json")
    @ApiOperation(value = "redis+訊息佇列進行秒殺實作", notes = "redis+訊息佇列進行秒殺實作", produces = "application/json")
    public RestResponse secKill(@RequestParam(value = "userName") String userName, @RequestParam(value = "stockName") String stockName) {
        return ResultGenerator.genSuccessResult(mqStockService.secKill(userName, stockName));
    }

    /**
     * 實作純資料庫操作實作秒殺操作
     *
     * @param userName  用戶名稱
     * @param stockName 商品名稱
     * @return {@link com.example.demo.result.RestResponse}
     * @author dispark
     * @date 2021/9/10 9:47 上午
     */
    @PostMapping(value = "sec-data-base", produces = "application/json;")
    @ApiOperation(value = "實作純資料庫操作實作秒殺操作", notes = "實作純資料庫操作實作秒殺操作", produces = "application/json")
    public RestResponse secDataBase(@RequestParam(value = "userName") String userName, @RequestParam(value = "stockName") String stockName) {
        return ResultGenerator.genSuccessResult(stockService.secDataBase(userName, stockName));
    }
}

啟動專案

啟動完成,redis中上架10個貨物,

在這里插入圖片描述

打開JMeter并發環境測驗

測驗計劃右鍵,添加一個執行緒組
在這里插入圖片描述
給這個執行緒組的數量為40,這個執行緒組的作用就是模擬40個用戶發送請求,去秒殺;然后再在執行緒組右鍵,添加一個Http請求,這個就是我們用來發送請求的組件了
在這里插入圖片描述在這里插入圖片描述
在這里插入圖片描述
這個請求唯一要說得就是,隨機引數了,因為用戶名肯定不可能給40個相同得名字,這邊我們利用JMeter給用戶名得值為亂數
點擊上方得白色小書本,選擇random,1-99得亂數,
在這里插入圖片描述
開始測驗
在這里插入圖片描述

log日志

2021-09-13 17:35:43,304 INFO [http-nio-8090-exec-10] o.a.c.c.C.[Tomcat].[localhost].[/] [DirectJDKLog.java : 173] Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-09-13 17:35:43,308 INFO [http-nio-8090-exec-10] o.s.web.servlet.DispatcherServlet [FrameworkServlet.java : 524] Initializing Servlet 'dispatcherServlet'
2021-09-13 17:35:43,335 INFO [http-nio-8090-exec-10] o.s.web.servlet.DispatcherServlet [FrameworkServlet.java : 546] Completed initialization in 26 ms
2021-09-13 17:35:43,401 INFO [http-nio-8090-exec-10] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:10,秒殺的商品是:watch
2021-09-13 17:35:43,404 INFO [http-nio-8090-exec-7] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:29,秒殺的商品是:watch
2021-09-13 17:35:43,403 INFO [http-nio-8090-exec-6] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:48,秒殺的商品是:watch
2021-09-13 17:35:43,402 INFO [http-nio-8090-exec-13] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:74,秒殺的商品是:watch
2021-09-13 17:35:43,404 INFO [http-nio-8090-exec-9] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:66,秒殺的商品是:watch
2021-09-13 17:35:43,407 INFO [http-nio-8090-exec-14] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:59,秒殺的商品是:watch
2021-09-13 17:35:43,402 INFO [http-nio-8090-exec-16] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:58,秒殺的商品是:watch
2021-09-13 17:35:43,406 INFO [http-nio-8090-exec-5] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:83,秒殺的商品是:watch
2021-09-13 17:35:43,408 INFO [http-nio-8090-exec-17] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:12,秒殺的商品是:watch
2021-09-13 17:35:43,408 INFO [http-nio-8090-exec-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:75,秒殺的商品是:watch
2021-09-13 17:35:43,403 INFO [http-nio-8090-exec-2] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:42,秒殺的商品是:watch
2021-09-13 17:35:43,405 INFO [http-nio-8090-exec-3] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:72,秒殺的商品是:watch
2021-09-13 17:35:43,403 INFO [http-nio-8090-exec-11] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:52,秒殺的商品是:watch
2021-09-13 17:35:43,405 INFO [http-nio-8090-exec-8] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:32,秒殺的商品是:watch
2021-09-13 17:35:43,403 INFO [http-nio-8090-exec-12] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:21,秒殺的商品是:watch
2021-09-13 17:35:43,403 INFO [http-nio-8090-exec-4] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:67,秒殺的商品是:watch
2021-09-13 17:35:43,404 INFO [http-nio-8090-exec-15] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:63,秒殺的商品是:watch
2021-09-13 17:35:43,424 INFO [http-nio-8090-exec-18] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:48,秒殺的商品是:watch
2021-09-13 17:35:43,429 INFO [http-nio-8090-exec-10] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:10秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,429 INFO [http-nio-8090-exec-7] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:29秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,430 INFO [http-nio-8090-exec-6] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:48秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,430 INFO [http-nio-8090-exec-11] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:52秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,430 INFO [http-nio-8090-exec-14] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:59秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-13] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:74秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-2] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:42秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-5] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:83秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-17] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:12秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-16] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 66] 用戶:58秒殺該商品:watch庫存有余,可以進行下訂單操作
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:75秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-8] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:32秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,431 INFO [http-nio-8090-exec-4] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:67秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,435 INFO [http-nio-8090-exec-12] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:21秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,435 INFO [http-nio-8090-exec-3] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:72秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,436 INFO [http-nio-8090-exec-18] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:48秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,435 INFO [http-nio-8090-exec-15] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:63秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,435 INFO [http-nio-8090-exec-9] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:66秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,455 INFO [http-nio-8090-exec-19] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:10,秒殺的商品是:watch
2021-09-13 17:35:43,464 INFO [http-nio-8090-exec-19] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:10秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,480 INFO [http-nio-8090-exec-20] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:49,秒殺的商品是:watch
2021-09-13 17:35:43,487 INFO [http-nio-8090-exec-20] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:49秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,502 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:43,798 INFO [http-nio-8090-exec-30] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:41,秒殺的商品是:watch
2021-09-13 17:35:43,798 INFO [http-nio-8090-exec-32] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:85,秒殺的商品是:watch
2021-09-13 17:35:43,799 INFO [http-nio-8090-exec-24] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:93,秒殺的商品是:watch
2021-09-13 17:35:43,799 INFO [http-nio-8090-exec-26] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:1,秒殺的商品是:watch
2021-09-13 17:35:43,800 INFO [http-nio-8090-exec-25] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:64,秒殺的商品是:watch
2021-09-13 17:35:43,802 INFO [http-nio-8090-exec-27] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:0,秒殺的商品是:watch
2021-09-13 17:35:43,804 INFO [http-nio-8090-exec-28] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:2,秒殺的商品是:watch
2021-09-13 17:35:43,804 INFO [http-nio-8090-exec-31] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:21,秒殺的商品是:watch
2021-09-13 17:35:43,804 INFO [http-nio-8090-exec-23] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:67,秒殺的商品是:watch
2021-09-13 17:35:43,804 INFO [http-nio-8090-exec-29] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:52,秒殺的商品是:watch
2021-09-13 17:35:43,811 INFO [http-nio-8090-exec-30] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:41秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,815 INFO [http-nio-8090-exec-24] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:93秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,816 INFO [http-nio-8090-exec-25] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:64秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,816 INFO [http-nio-8090-exec-27] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:0秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,816 INFO [http-nio-8090-exec-28] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:2秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,816 INFO [http-nio-8090-exec-31] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:21秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,816 INFO [http-nio-8090-exec-23] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:67秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,815 INFO [http-nio-8090-exec-32] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:85秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,815 INFO [http-nio-8090-exec-26] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:1秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,816 INFO [http-nio-8090-exec-29] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:52秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,818 INFO [http-nio-8090-exec-22] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:45,秒殺的商品是:watch
2021-09-13 17:35:43,818 INFO [http-nio-8090-exec-33] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:21,秒殺的商品是:watch
2021-09-13 17:35:43,831 INFO [http-nio-8090-exec-34] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:99,秒殺的商品是:watch
2021-09-13 17:35:43,846 INFO [http-nio-8090-exec-22] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:45秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,848 INFO [http-nio-8090-exec-33] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:21秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,848 INFO [http-nio-8090-exec-21] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:34,秒殺的商品是:watch
2021-09-13 17:35:43,848 INFO [http-nio-8090-exec-4] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:1,秒殺的商品是:watch
2021-09-13 17:35:43,855 INFO [http-nio-8090-exec-34] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:99秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,857 INFO [http-nio-8090-exec-21] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:34秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,862 INFO [http-nio-8090-exec-4] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:1秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,877 INFO [http-nio-8090-exec-31] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:32,秒殺的商品是:watch
2021-09-13 17:35:43,887 INFO [http-nio-8090-exec-31] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:32秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,900 INFO [http-nio-8090-exec-20] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:68,秒殺的商品是:watch
2021-09-13 17:35:43,907 INFO [http-nio-8090-exec-20] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:68秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,925 INFO [http-nio-8090-exec-13] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:26,秒殺的商品是:watch
2021-09-13 17:35:43,929 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:58,商品名稱為:watch
2021-09-13 17:35:43,933 INFO [http-nio-8090-exec-13] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:26秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,948 INFO [http-nio-8090-exec-9] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:52,秒殺的商品是:watch
2021-09-13 17:35:43,955 INFO [http-nio-8090-exec-9] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:52秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:43,973 INFO [http-nio-8090-exec-6] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 60] 參加秒殺的用戶是:96,秒殺的商品是:watch
2021-09-13 17:35:43,981 INFO [http-nio-8090-exec-6] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 78] 用戶:96秒殺時商品的庫存量沒有剩余,秒殺結束
2021-09-13 17:35:44,118 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,118 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:52,商品名稱為:watch
2021-09-13 17:35:44,126 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,134 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,135 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:29,商品名稱為:watch
2021-09-13 17:35:44,143 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,151 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,151 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:42,商品名稱為:watch
2021-09-13 17:35:44,160 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,169 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:59,商品名稱為:watch
2021-09-13 17:35:44,169 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,187 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,192 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:83,商品名稱為:watch
2021-09-13 17:35:44,194 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] c.e.d.s.impl.MQStockServiceImpl [MQStockServiceImpl.java : 45] 庫存訊息佇列收到的訊息商品資訊是:watch
2021-09-13 17:35:44,208 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:10,商品名稱為:watch
2021-09-13 17:35:44,223 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:12,商品名稱為:watch
2021-09-13 17:35:44,239 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:48,商品名稱為:watch
2021-09-13 17:35:44,254 INFO [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] c.e.d.s.impl.MQOrderServiceImpl [MQOrderServiceImpl.java : 33] 收到訂單訊息,訂單用戶為:74,商品名稱為:watch

秒殺完成了,但是redis中出現超賣現象,
在這里插入圖片描述
存在超賣問題,正在優化中,后續繼續更新…

原始碼:碼云倉庫
訪問地址:https://gitee.com/kai-java/sec-kill
git地址:https://gitee.com/kai-java/sec-kill.git
所有內容皆為個人總結或轉載別人的文章,只為學習技術, 若您覺得文章有用,歡迎點贊分享! 若無意對您的文章造成侵權,請您留言,博主看到后會及時處理,謝謝,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/299946.html

標籤:其他

上一篇:玩轉MYSQL(2) 人類高質量資料庫的約束、聚合查詢、聯合查詢以及三種表的設計

下一篇:老司機帶帶我:數倉建模架構|維度建模剖析與案例演示

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more