实战深入了解redis+消息队列如何实现秒杀

news2024/10/7 16:16:30

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
所有内容皆为个人总结或转载别人的文章,只为学习技术。 若您觉得文章有用,欢迎点赞分享! 若无意对您的文章造成侵权,请您留言,博主看到后会及时处理,谢谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/668185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

配置NIS服务器及客户端

在服务端安装所需软件包 设置主机名和NIS域名 编辑 NIS服务器主配置文件 最下面编辑访问控制 建立测试用户 配置NFS&#xff0c;否则客户端切换用户时&#xff0c;用户没有家目录 安装NFS所需软件包 Nfs-utils 给两个共享目录权限&#xff0c;编辑NFS配制文件 共享两个目录 重…

12.JavaWeb-Spring Boot + 物业管理项目

1.Spring Boot 概念 自动处理应用程序幕后的各种杂事&#xff08;Spring框架的应用程序的配置和部署过程&#xff09;&#xff0c;让你专注于做那些使应用程序独特的工作 1.1 Spring Boot的核心功能 1.1.1 自动配置 Spring Boot根据应用程序的依赖和配置信息&#xff0…

计算机网络——自顶向下方法(第二章学习记录)

本章学习应用层 网络应用是计算机网络存在的理由。 网络应用程序体系结构 现代网络应用程序有两种主流体系结构&#xff1a;客户—服务器体系结构和对等(P2P)体系结构 客户—服务器体系结构&#xff08;client-server )&#xff0c;在这个结构中&#xff0c;有一个总是打开的…

SAP CAP篇五:为CAP添加Fiori Elements程序(2)

本文目录 本系列之前的文章新建Fiori Elements Application选择Application TypeData SourceEntity SelectionProject InformationLaunchpad Setting 修改manage-books文件夹子文件夹 webapp重命名 annotations.cds更新fiori-service.cdsmanifest.json 添加services.cds到app文…

哈工大计算机网络课程传输层协议之:拥塞控制原理剖析

哈工大计算机网络课程传输层协议之&#xff1a;拥塞控制原理剖析 文章目录 哈工大计算机网络课程传输层协议之&#xff1a;拥塞控制原理剖析拥塞成因和代价&#xff1a;场景1拥塞成因和代价&#xff1a;场景2拥塞成因和代价&#xff1a;场景3如何进行拥塞控制拥塞控制的方法TCP…

哈工大计算机网络课程网络层协议之:网络层服务概述

哈工大计算机网络课程网络层协议之&#xff1a;网络层服务概述 文章目录 哈工大计算机网络课程网络层协议之&#xff1a;网络层服务概述网络层概述网络层核心功能—转发与路由网络层核心功能—连接建立网络层服务模型虚电路网络与数据报网络虚电路网络虚电路(VC)的具体实现VC转…

Web 渗透测试攻防之浅述信息收集

前言 众所周知渗透测试的本质是信息收集&#xff0c;在渗透测试中信息收集的质量直接关系到渗透测试成果的与否。在对系统进行渗透测试前的信息收集是通过各种方式获取所需要的信息&#xff0c;收集的信息越多对目标进行渗透的优势越有利。通过利用获取到的信息对系统进行渗透…

【前端布局篇】响应式布局 Bootstrap 移动端布局

前言 1. 布局介绍 布局:layout 对事物的全面规划和安排 页面布局&#xff1a;对页面的文字、图形或表格进行格式设置。包括字体、字号、颜色纸张大小和方向以及页边距等。 网页布局&#xff1a;利用html搭建结构与内容&#xff0c;使用CSS添加装饰 网页布局有很多种方式&a…

Flink 学习七 Flink 状态(flink state)

Flink 学习七 Flink 状态(flink state) 1.状态简介 流式计算逻辑中,比如sum,max; 需要记录和后面计算使用到一些历史的累计数据, 状态就是:用户在程序逻辑中用于记录信息的变量 在Flink 中 ,状态state 不仅仅是要记录状态;在程序运行中如果失败,是需要重新恢复,所以这个状态…

基于Django的疫情困扰下的民慧钢材销售分析及纾困策略-计算机毕设 附源码87656

基于Django的疫情困扰下的民慧钢材销售分析及纾困策略 摘 要 疫情之下&#xff0c;实体经济面临下行压力。2019年以来&#xff0c;新冠肺炎疫情卷土而来&#xff0c;各地地疫情防控形势严峻&#xff0c;许多中小微企业经营发展屡次遭受打击。面对疫情常态化的社会现实&#x…

[学习笔记] [机器学习] 13. 集成学习进阶(XGBoost、OTTO案例实现、LightGBM、PUBG玩家排名预测)

视频链接数据集下载地址&#xff1a;无需下载 学习目标&#xff1a; 知道 XGBoost 算法原理知道 otto 案例通过 XGBoost 实现流程知道 LightGBM 算法原理知道 PUBG 案例通过 LightGBM 实现流程知道 Stacking 算法原理知道住房月租金预测通过 Stacking 实现流程 1. XGBoost 算…

SPI协议(嵌入式学习)

SPI协议 概念时序SPI通信模式图四种通信模式 优缺点 概念 SPI&#xff08;Serial Peripheral Interface&#xff09;是一种串行外设接口协议&#xff0c;用于在数字系统之间进行通信。它被广泛应用于嵌入式系统和电子设备中&#xff0c;用于连接微控制器、传感器、存储器、显示…

Linux权限管理(超详解哦)

Linux权限 引言文件访问者的分类文件类型与访问权限文件类型访问权限 文件权限值的表示方法修改权限的指令chmod修改文件权限通过角色/-/权限来修改通过三个八进制数修改 chown修改所有者chgrp修改所属组umask修改或查看文件权限掩码文件创建时的权限 目录的权限粘滞位 总结 引…

【命令参数】SVN - 环境配置及常用命令参数

目录 环境配置 基本语法 参数指令 SVN是一款基于C/S架构的版本控制系统&#xff0c;能够实现对产品项目的版本托管以及对源码库的高效管理。而掌握SVN中的一些命令参数&#xff0c;一定程度上可以使日常效率得到进一步提升。 环境配置 为在调用时更加便捷&#xff0c;通常会…

我们如何实现业务操作日志功能?

1. 需求 我们经常会有这样的需求&#xff0c;需要对关键的业务功能做操作日志记录&#xff0c;也就是用户在指定的时间操作了哪个功能&#xff0c;操作前后的数据记录&#xff0c;必要的时候可以一键回退&#xff0c;今天我就为大家实现这个的功能&#xff0c;让大家可以直接拿…

哈尔滨工业大学计算机考研分析

关注我们的微信公众号 姚哥计算机考研 更多详情欢迎咨询 哈尔滨工业大学&#xff08;A&#xff09;考研难度&#xff08;☆☆☆☆☆&#xff09; 哈尔滨工业大学计算机考研招生学院是计算学部、计算学部&#xff08;深圳&#xff09;和计算学部&#xff08;威海&#xff09;…

C++完成烧烤节管理系统

背景&#xff1a; 这次我们结合今年淄博烧烤做一个餐厅管理系统&#xff0c;具体需求如下&#xff0c;我们选择的是餐饮商家信息管理 问题描述&#xff1a; 淄博烧烤今年大火&#xff0c;“进淄赶烤”是大家最想干的事情&#xff0c;淄博烧烤大火特火的原因&#xff0c;火的…

C语言之文件的读写(1)

前面三部分已经给大家介绍过了&#xff0c;网址发给大家方便大家复习 打开方式如下&#xff1a; 文件使用方式 含义 如果指定文件不存在 “r”&#xff08;只读&#xff09; 为了输入数据&#xff0c;打开一个已经存在的文本文件 出错 “w”&#xff08;只写&#xff09; 为了输…

文心一言眼里的Java世界

目录 一、Java基础教程系列二、先听听文心一言怎么说&#xff1f;三、话不多说&#xff0c;开干。1、要有一个正确的Java学习路线&#xff0c;做一个细致的Java学习规划。2、学习资料推荐3、书中自有黄金屋&#xff0c;书中自有颜如玉4、自学周期推荐5、效率为先6、哪吒的学习方…