Using spring boot + mybatis + RabbitMQ + redis to simulate the second kill function of the mall

Keywords: RabbitMQ Redis Mybatis Database

Using spring boot + mybatis + RabbitMQ + redis to simulate the second kill function of the mall

Thank you for watching. I hope it's useful for readers. I don't say much. I'll explain it directly

First, we need to install RabbitMQ. Before installing RabbitMQ, we need to install Erlang, because RabbitMQ is based on Erlang. For specific steps, click the following link:

Click here: detailed steps to install Erlang and RabbitMQ

After installation, RabbitMQ is as follows:


After installation, you can enter: http://localhost:15672/#/users. The default account and password are both guset

After the environment is set up, you can start the code rolling

Since I am in maven environment, I will directly use jar coordinates here
rabbitMQ

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
        <version>1.5.8.RELEASE</version>
    </dependency>

redis

	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

mybatis

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.1</version>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator</artifactId>
        <version>1.3.5</version>
        <type>pom</type>
    </dependency>

    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.3.5</version>
    </dependency>

Create test database structure

– Table structure for stock

DROP TABLE IF EXISTS stock;
CREATE TABLE stock (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
stock varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

– Table structure for t_order

DROP TABLE IF EXISTS t_order;
CREATE TABLE t_order (
id int(11) NOT NULL AUTO_INCREMENT,
order_name varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
order_user varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

Create the corresponding dao mapper xml

After creation, first create a listener initialization data to redis, which can be changed according to its own business logic

package com.ceshi.demo.service;

import com.ceshi.demo.dao.StockMapper;
import com.ceshi.demo.pojo.Stock;
import com.ceshi.demo.pojo.StockExample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;

/**
 * @author nonpool
 * @version 1.0
 * @since 2020/2/11
 */
@Component
@Slf4j
public class ApplicationInitListener implements ApplicationListener<ContextRefreshedEvent> {
    @Resource
    private StockMapper stockMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        // Get the application context first
        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        log.info(">>>>>>>>>>>>Project initialization complete, execute the logic in the listener");
            //The sql in mapper returns the collection of all goods on the shelf (supporting seckill)
            StockExample stockExample = new StockExample();
            List<Stock> stocks = stockMapper.selectByExample(stockExample);
            Iterator<Stock> it = stocks.iterator();
            while(it.hasNext()) {
                Stock p = it.next();
                log.info("Commodity name:"+p.getName()+"Merchandise inventory:"+p.getStock());
                try {
                    stringRedisTemplate.opsForValue().set(String.valueOf(p.getName()), String.valueOf(p.getStock()));
                } catch (Exception e) {
                    log.error("Current product name:"+p.getName()+"Stock:"+p.getStock()+"Put in Redis Cache exceptions<<<<<<<<<<<<<<<<<<<<");
                    e.printStackTrace();
                }
            }
    }
}

Configure rabbitMQConfig queue

package com.ceshi.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 Configure rabbitMQConfig queue
 * @version 1.0
 * @since 2020/2/11
 */
@Configuration
public class RabbitMQCongig {
    //Inventory switch
    public static final String STORY_EXCHANGE = "STORY_EXCHANGE";

    //Order switch
    public static final String ORDER_EXCHANGE = "ORDER_EXCHANGE";

    //Inventory queue
    public static final String STORY_QUEUE = "STORY_QUEUE";

    //Order queue
    public static final String ORDER_QUEUE = "ORDER_QUEUE";

    //Inventory routing key
    public static final String STORY_ROUTING_KEY = "STORY_ROUTING_KEY";

    //Order routing key
    public static final String ORDER_ROUTING_KEY = "ORDER_ROUTING_KEY";
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
    //Create inventory switch
    @Bean
    public Exchange getStoryExchange() {
        return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();
    }
    //Create inventory queue
    @Bean
    public Queue getStoryQueue() {
        return new Queue(STORY_QUEUE);
    }
    //Inventory switch and inventory queue binding
    @Bean
    public Binding bindStory() {
        return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();
    }
    //Create order queue
    @Bean
    public Queue getOrderQueue() {
        return new Queue(ORDER_QUEUE);
    }
    //Create order switch
    @Bean
    public Exchange getOrderExchange() {
        return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();
    }
    //Order queue is bound to order switch
    @Bean
    public Binding bindOrder() {
        return BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();
    }

}

Create MQ listening order message queue and consume

package com.ceshi.demo.service;

import com.ceshi.demo.config.RabbitMQCongig;
import com.ceshi.demo.dao.TOrderMapper;
import com.ceshi.demo.pojo.TOrder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author 
 * @version 1.0
 * @since 2020/2/11
 */
@Service
@Slf4j
public class MQOrderService {
    @Resource
    private TOrderMapper orderMapper;
    /**
     * Listen to the order message queue and consume
     *
     * @param order
     */
    @RabbitListener(queues = RabbitMQCongig.ORDER_QUEUE)
    public void createOrder(TOrder order) {
        log.info("Received the order message. The order user is:{},The commodity name is:{}", order.getOrderUser(), order.getOrderName());
        /**
         * Call database orderService to create order information
         */
        orderMapper.insert(order);
    }
}

MQ creates a listening inventory message queue and consumes

package com.ceshi.demo.service;

import com.ceshi.demo.config.RabbitMQCongig;
import com.ceshi.demo.dao.StockMapper;
import com.ceshi.demo.pojo.Stock;
import com.ceshi.demo.pojo.StockExample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author 
 * @version 1.0
 * @since 2020/2/11
 * Monitor the consumer goods in the message queue for consumption
 */
@Service
@Slf4j
public class MQStockService {
    //Commodity table in consumption database
    @Resource
    private StockMapper stockMapper;
    /**
     * Listen to inventory message queue and consume
     * @param stockName
     */
    @RabbitListener(queues = RabbitMQCongig.STORY_QUEUE)
    public void decrByStock(String stockName) {
        log.info("The message commodity information received by the inventory message queue is:{}", stockName);
        /**
         * Call the database service to reduce the inventory of the corresponding goods in the database by one
         */
        //Get the quantity of the current product
        StockExample stockExample = new StockExample();
        StockExample.Criteria criteria = stockExample.createCriteria();
        criteria.andNameEqualTo(stockName);
        List<Stock> stocks = stockMapper.selectByExample(stockExample);
        //modify
        Stock stock = new Stock();
        stock.setId(stocks.get(0).getId());
        stock.setName(stocks.get(0).getName());
        stock.setStock((Integer.parseInt(stocks.get(0).getStock())-1)+"");

        stockMapper.updateByPrimaryKey(stock);
    }
}

Create reidservice

package com.ceshi.demo.service;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author chy
 * @version 1.0
 * @since 2020/2/11
 * Set redisService
 */
@Service
public class RedisService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * Subtract one from the key value of the specified key
     * @param key
     * @return
     */
    public Long decrBy(String key) {
        return stringRedisTemplate.opsForValue().decrement(key);
    }
}

Finally, create the controller

package com.ceshi.demo.controller;

import com.ceshi.demo.config.RabbitMQCongig;
import com.ceshi.demo.dao.StockMapper;
import com.ceshi.demo.dao.TOrderMapper;
import com.ceshi.demo.pojo.Stock;
import com.ceshi.demo.pojo.StockExample;
import com.ceshi.demo.pojo.TOrder;
import com.ceshi.demo.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author nonpool
 * @version 1.0
 * @since 2020/2/11
 */
@RestController
@RequestMapping("activity")
@ResponseBody
@Slf4j
public class Ceshi {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisService redisService;
    @Resource
    private TOrderMapper tOrderMapper;
    @Resource
    private StockMapper stockMapper;
    /**
     * Using redis + message queue for seckill
     *
     * @param username
     * @param stockName
     * @return
     */
    @RequestMapping("sec")
    @ResponseBody
    public String sec(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {

        log.info("Users participating in seckill are:{},Seckill's products are:{}", username, stockName);
        String message = null;
        //Call redis to reduce the inventory of corresponding goods by one
        Long decrByResult = redisService.decrBy(stockName);
        if (decrByResult >= 0) {
            /**
             * This indicates that there is a surplus in the inventory of this product. You can place an order
             */
            log.info("User:{}Seckill this product:{}Inventory surplus, order can be placed", username, stockName);
            //Send a message to the inventory message queue to reduce the inventory data by one
            rabbitTemplate.convertAndSend(RabbitMQCongig.STORY_EXCHANGE, RabbitMQCongig.STORY_ROUTING_KEY, stockName);

            //Send message to order message queue, create order
            TOrder order = new TOrder();
            order.setOrderName(stockName);
            order.setOrderUser(username);
            rabbitTemplate.convertAndSend(RabbitMQCongig.ORDER_EXCHANGE, RabbitMQCongig.ORDER_ROUTING_KEY, order);
            message = "user" + username + "Seckill" + stockName + "Success";
        } else {
            /**
             * It indicates that there is no surplus in the inventory of this product, and the message of seckill failure will be returned to the user directly
             */
            log.info("User:{}There is no surplus of goods in stock at seckill,End of spike", username);
            message = "User:"+ username + "There is no surplus of goods in stock,End of spike";
        }
        return message;
    }


    /**
     * Realize pure database operation, realize seckill operation
     * @param username
     * @param stockName
     * @return
     */
    @RequestMapping("secDataBase")
    @ResponseBody
    public String secDataBase(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {
        log.info("Users participating in seckill are:{},Seckill's products are:{}", username, stockName);
        String message = null;
        //Find the inventory of this product
        StockExample stockExample = new StockExample();
        StockExample.Criteria criteria = stockExample.createCriteria();
        criteria.andNameEqualTo(stockName);
        List<Stock> stocks = stockMapper.selectByExample(stockExample);
        Integer stockCount = Integer.parseInt(stocks.get(0).getStock());
        log.info("User:{}Participate in seckill. The current inventory of goods is:{}", username, stockCount);
        if (stockCount > 0) {
            /**
             * There is also inventory. You can continue to kill, reduce inventory by one, and place an order
             */
            //1. Inventory minus one
            Stock stock = new Stock();
            stock.setId(stocks.get(0).getId());
            stock.setStock((stockCount-1)+"");
            stock.setName(stocks.get(0).getName());

            stockMapper.updateByPrimaryKey(stock);
            //2. Place an order
            TOrder order = new TOrder();
            order.setOrderUser(username);
            order.setOrderName(stockName);
            tOrderMapper.insert(order);
            log.info("User:{}.The result of taking part in the second kill: success", username);
            message = username + "The result of taking part in the second kill: success";
        } else {
            log.info("User:{}.The result of taking part in the second kill: the second kill is over", username);
            message = username + "The result of taking part in seckill activity is: seckill is over";
        }
        return message;
    }
}

At this end, what's better for you readers? Let's have a common exchange. I hope you can give us more guidance on the shortcomings.

Published 4 original articles, won praise 6, visited 9885
Private letter follow

Posted by newjsguy on Tue, 11 Feb 2020 01:49:52 -0800