Using spring boot + mybatis + RabbitMQ + redis to simulate the second kill function of the mall
- 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:
- After installation, RabbitMQ is as follows:
- After the environment is set up, you can start the code rolling
- Create test database structure
- 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
- Configure rabbitMQConfig queue
- Create MQ listening order message queue and consume
- MQ creates a listening inventory message queue and consumes
- Create reidservice
- Finally, create the controller
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 TABLEstock
(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 TABLEt_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.