SpringBoot + Redis + RabbitMQ 实现高并发限时秒杀

1、安装RabbitMQ

docker安装:


windows安装:


2、安装Redis

docker安装:


windows安装:


springboot整合redis:


3、安装Jmeter测试工具

windows安装:


4、数据库设计

4、1 商品库存表: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=utf8 ROW_FORMAT=DYNAMIC COMMENT='商品库存表';
4、2 秒杀订单表: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=utf8 ROW_FORMAT=DYNAMIC COMMENT='秒杀订单表';

5、JAVA代码

5、1 pom引入
<!--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>
5、2 配置application.xml
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:13306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: root1234
    # 使用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: 127.0.0.1
    port: 6379
    password: 12345678
  rabbitmq:  #mq配置
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
server:
  port: 8090
logging:
  config: classpath:logback-spring.xml
5、3 实体类

Order 秒杀订单表

package com.example.demo.entity;
 
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
 
/**
 * @author fj
 * @Description: 订单
 * @date 2022-07-01 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;
}

Stock 商品库存表

package com.example.demo.entity;
 
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
 
/**
 * @author fj
 * @Description: 商品库存表
 * @date 2022-07-01 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;
}
5、4 服务层

StockService存货服务层

package com.example.demo.service;
 
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Stock;
 
/**
 * @author fj
 * @Description: 存货服务层
 * @date 2022-07-01 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);
}

OrderService订单服务层

package com.example.demo.service;
 
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.Order;
 
/**
 * @author fj
 * @Description: 订单服务层
 * @date 2022-07-01 15:54
 */
public interface OrderService extends IService<Order> {
    /**
     * 订单保存
     * @param order 实体
     */
    void saveOrder(Order order);
}

OrderServiceImpl订单实现层

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 fj
 * @Description: 订单实现层
 * @date 2022-07-01 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);
        }
    }
}

StockServiceImpl存货实现层

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 fj
 * @Description: 存货实现层
 * @date 2022-07-07 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;
    }
}
5、5 配置rabbitmq的实现方式以及redis的实现方式

1)在 service包下面直接新建 MQOrderServiceImpl.java,这个类属于订单的消费队列。

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 fj
 * @Description: MQ订单实现层
 * @date 2022-07-07 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);
    }
}

2)MQStockServiceImpl.java这个属于库存得消费队列。

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 fj
 * @Description: MQ存货实现层
 * @date 202-07-07 15:54
 */
@Service
@Slf4j
public class MQStockServiceImpl {
 
    @Autowired
    private StockService stockService;
 
    @Autowired
    private RabbitTemplate rabbitTemplate;
 
    @Autowired
    private RedisUtil redisUtil;
 
 
    /**
     * 秒杀商品后-减少库存
     * @param name 商品名称
     */
    @RabbitListener(queues = RabbitMqConfig.STORY_QUEUE)
    public void decrByStock(String name) {
        log.info("库存消息队列收到的消息商品信息是:{}", name);
        /**
         * 调用数据库service给数据库对应商品库存减一
         */
        stockService.decrByStock(name);
    }
 
    /**
     * 使用redis+消息队列进行秒杀实现
     * @param userName 用户名称
     * @param stockName 商品名称
     * @return String
     */
    public String secKill(String userName,String stockName) {
        log.info("参加秒杀的用户是:{},秒杀的商品是:{}", userName, stockName);
        String message = "";
        //调用redis给相应商品库存量减一
        Long decrByResult = redisUtil.decrBy(stockName);
        if (decrByResult >= 0) {
            /**
             * 说明该商品的库存量有剩余,可以进行下订单操作
             */
            log.info("用户:{}秒杀该商品:{}库存有余,可以进行下订单操作", userName, stockName);
            //发消息给库存消息队列,将库存数据减一
            rabbitTemplate.convertAndSend(RabbitMqConfig.STORY_EXCHANGE, RabbitMqConfig.STORY_ROUTING_KEY, stockName);
 
            //发消息给订单消息队列,创建订单
            Order order = new Order();
            order.setOrderName(stockName);
            order.setOrderUser(userName);
            rabbitTemplate.convertAndSend(RabbitMqConfig.ORDER_EXCHANGE, RabbitMqConfig.ORDER_ROUTING_KEY, order);
            message = "用户" + userName + "秒杀" + stockName + "成功";
        } else {
            /**
             * 说明该商品的库存量没有剩余,直接返回秒杀失败的消息给用户
             */
            log.info("用户:{}秒杀时商品的库存量没有剩余,秒杀结束", userName);
            message = "用户:"+ userName + "商品的库存量没有剩余,秒杀结束";
        }
        return message;
    } 
}

6、RabbitMqConfig 和redisUtil工具类

1.RabbitMqConfig.java

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 fj
 * @Description: RabbitMQConfig插件配置
 * @date 2022-07-07 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();
    }
 
}

2.RedisCacheConfig.java

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: fj
 * @date: 2022年07月08日 下午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;
    }
}

3.RedisUtil.java部分代码

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

7、 controller提供了二个方法,一个为redis+rabbitmq实现高并发秒杀,第二个则用纯数据库模拟秒杀,出现超卖现象。

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 fj
 * @Description: 秒杀
 * @date 2022-07-08 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 String
     */
    @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 String
     */
    @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));
    }
 
 
}

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

package com.example.demo;
 
 
import com.example.demo.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 
@SpringBootApplication
public class DemoApplication implements ApplicationRunner {
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        /*SpringApplication springApplication = new SpringApplication(DemoApplication.class);
        ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
        //解决WebSocket不能注入的问题  第一种方法
        WebSocketServerController.setApplicationContext(configurableApplicationContext);*/
    }
 
    @Autowired
    private RedisUtil redisUtil;
 
    /**
     * redis初始化商品的库存量和信息
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        redisUtil.setValue("watch", 10, (long)20000);
    }
 
}