#使用idea创建springboot +Redis +RabbitMQ 实现高并发限时秒杀

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

1.安装RabbitMQ

安装路径: RabbitMQ安装教程

2.安装Redis +RedisDesktopManager

安装路径:
Redis和RedisDesktopManager安装教程

3.安装 Jmeter测试工具

安装路径:
Jmeter安装教程

1.第一步先新建Springboot项目

2.导jar包
pom.xml

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            org.apache.commons
            commons-lang3
            3.8.1
        
        
            org.springframework.boot
            spring-boot-starter-amqp
        
        
            io.jsonwebtoken
            jjwt
            0.7.0
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.0
        
        
            org.springframework
            spring-tx
        
        
            tk.mybatis
            mapper-spring-boot-starter
            2.0.3-beta1
        
        
            tk.mybatis
            mapper
            4.0.0
        
    

3.配置application.properties

spring.devtools.restart.enabled=false
##配置数据库连接
spring.datasource.username=root
spring.datasource.password=root
server.port=8443
spring.datasource.url=jdbc:mysql://localhost:3306/ktao?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

##配置rabbitmq连接  --先打开服务,不然会报错
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

##配置连接redis --先打开服务,不然会报错
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.jedis.pool.max-active=1024
spring.redis.jedis.pool.max-wait=-1s
spring.redis.jedis.pool.max-idle=200
spring.redis.password=123456

4.实体类 Order.java

package com.example.springbootxsmsha.pojo;

import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Data
@Table(name = "t_order")
public class Order implements Serializable {
    private static final long serialVersionUID = -8867272732777764701L;

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

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

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

Stock.java

package com.example.springbootxsmsha.pojo;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Table(name = "stock")
@Data
public class Stock implements Serializable {
    private static final long serialVersionUID = 2451194410162873075L;

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

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

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

4.Mapper 文件
OrderMapper.java
StockMapper.java如下一样 在此省略

package com.example.springbootxsmsha.mapper;

import com.example.springbootxsmsha.base.service.GenericMapper;
import com.example.springbootxsmsha.pojo.Order;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper extends GenericMapper {
    void insertOrder(Order order);
}

5.Service层

package com.example.springbootxsmsha.service;

import com.example.springbootxsmsha.config.MyRabbitMQConfig;
import com.example.springbootxsmsha.pojo.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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


package com.example.springbootxsmsha.service;

import com.example.springbootxsmsha.config.MyRabbitMQConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

package com.example.springbootxsmsha.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

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

@Service
public class RedisService {
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 设置String键值对
     * @param key
     * @param value
     * @param millis
     */
    public void put(String key, Object value, long millis) {
        redisTemplate.opsForValue().set(key, value, millis, TimeUnit.MINUTES);
    }
    public void putForHash(String objectKey, String hkey, String value) {
        redisTemplate.opsForHash().put(objectKey, hkey, value);
    }
    public  T get(String key, Class type) {
        return (T) redisTemplate.boundValueOps(key).get();
    }
    public void remove(String key) {
        redisTemplate.delete(key);
    }
    public boolean expire(String key, long millis) {
        return redisTemplate.expire(key, millis, TimeUnit.MILLISECONDS);
    }
    public boolean persist(String key) {
        return redisTemplate.hasKey(key);
    }
    public String getString(String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }
    public Integer getInteger(String key) {
        return (Integer) redisTemplate.opsForValue().get(key);
    }
    public Long getLong(String key) {
        return (Long) redisTemplate.opsForValue().get(key);
    }
    public Date getDate(String key) {
        return (Date) redisTemplate.opsForValue().get(key);
    }

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

6.实现类 接口省略

package com.example.springbootxsmsha.service.impl;


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

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

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

package com.example.springbootxsmsha.service.impl;

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

import java.util.List;

@Service
public class StockServiceImpl implements StockService {
    @Autowired
    private StockMapper stockMapper;
    // 秒杀商品后减少库存
    @Override
    public void decrByStock(String stockName) {
        Example example = new Example(Stock.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("name", stockName);
        List stocks = stockMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(stocks)) {
            Stock stock = stocks.get(0);
            stock.setStock(stock.getStock() - 1);
            stockMapper.updateByPrimaryKey(stock);
        }
    }
    // 秒杀商品前判断是否有库存
    @Override
    public Integer selectByExample(String stockName) {
        Example example = new Example(Stock.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("name", stockName);
        List stocks = stockMapper.selectByExample(example);
        if (!CollectionUtils.isEmpty(stocks)) {
            return stocks.get(0).getStock().intValue();
        }
        return 0;
    }
}


7.controller层

package com.example.springbootxsmsha.controller;

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

@Controller
@Slf4j
public class SecController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisService redisService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private StockService stockService;
    /**
     * 使用redis+消息队列进行秒杀实现
     *
     * @param username
     * @param stockName
     * @return
     */
    @PostMapping( value = "/sec",produces = "application/json;charset=utf-8")
    @ResponseBody
    public String sec(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {

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

            //发消息给订单消息队列,创建订单
            Order order = new Order();
            order.setOrder_name(stockName);
            order.setOrder_user(username);
            rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE, MyRabbitMQConfig.ORDER_ROUTING_KEY, order);
            message = "用户" + username + "秒杀" + stockName + "成功";
        } else {
            /**
             * 说明该商品的库存量没有剩余,直接返回秒杀失败的消息给用户
             */
            log.info("用户:{}秒杀时商品的库存量没有剩余,秒杀结束", username);
            message = "用户:"+ username + "商品的库存量没有剩余,秒杀结束";
        }
        return message;
    }
    /**
     * 实现纯数据库操作实现秒杀操作
     * @param username
     * @param stockName
     * @return
     */
    @RequestMapping("/secDataBase")
    @ResponseBody
    public String secDataBase(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {
        log.info("参加秒杀的用户是:{},秒杀的商品是:{}", username, stockName);
        String message = null;
        //查找该商品库存
        Integer stockCount = stockService.selectByExample(stockName);
        log.info("用户:{}参加秒杀,当前商品库存量是:{}", username, stockCount);
        if (stockCount > 0) {
            /**
             * 还有库存,可以进行继续秒杀,库存减一,下订单
             */
            //1、库存减一
            stockService.decrByStock(stockName);
            //2、下订单
            Order order = new Order();
            order.setOrder_user(username);
            order.setOrder_name(stockName);
            orderService.insertOrder(order);
            log.info("用户:{}.参加秒杀结果是:成功", username);
            message = username + "参加秒杀结果是:成功";
        } else {
            log.info("用户:{}.参加秒杀结果是:秒杀已经结束", username);
            message = username + "参加秒杀活动结果是:秒杀已经结束";
        }
        return message;
    }
}


8.还有两个工具类

package com.example.springbootxsmsha.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
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;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MyRabbitMQConfig {

    //库存交换机
    public static final String STORY_EXCHANGE = "STORY_EXCHANGE";

    //订单交换机
    public static final String ORDER_EXCHANGE = "ORDER_EXCHANGE";

    //库存队列
    public static final String STORY_QUEUE = "STORY_QUEUE";

    //订单队列
    public static final String ORDER_QUEUE = "ORDER_QUEUE";

    //库存路由键
    public static final String STORY_ROUTING_KEY = "STORY_ROUTING_KEY";

    //订单路由键
    public static final String ORDER_ROUTING_KEY = "ORDER_ROUTING_KEY";
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
    //创建库存交换机
    @Bean
    public Exchange getStoryExchange() {
        return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();
    }
    //创建库存队列
    @Bean
    public Queue getStoryQueue() {
        return new Queue(STORY_QUEUE);
    }
    //库存交换机和库存队列绑定
    @Bean
    public Binding bindStory() {
        return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();
    }
    //创建订单队列
    @Bean
    public Queue getOrderQueue() {
        return new Queue(ORDER_QUEUE);
    }
    //创建订单交换机
    @Bean
    public Exchange getOrderExchange() {
        return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();
    }
    //订单队列与订单交换机进行绑定
    @Bean
    public Binding bindOrder() {
        return BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();
    }
}

package com.example.springbootxsmsha.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    // 配置redis得配置详解
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

  1. 配置tkmybatis得接口
    新建一个base包,在base下面新建service接口
package com.example.springbootxsmsha.base.service;

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

public interface GenericMapper extends Mapper, MySqlMapper {
}

10.设置启动类

package com.example.springbootxsmsha;

import com.example.springbootxsmsha.service.RedisService;
import org.mybatis.spring.annotation.MapperScan;
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
@MapperScan("com.example.springbootxsmsha.mapper")
public class SpringbootxsmshaApplication  implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootxsmshaApplication.class, args);
    }
    @Autowired
    private RedisService redisService;
    /**
     * redis初始化商品的库存量和信息
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        redisService.put("watch", 10, 20);
    }
}


最后测试

#使用idea创建springboot +Redis +RabbitMQ 实现高并发限时秒杀_第1张图片

你可能感兴趣的:(#使用idea创建springboot +Redis +RabbitMQ 实现高并发限时秒杀)