使用springboot redis RabbitMQ实现商品秒杀
2020-01-15 10:58
836 查看
前言
在电商项目中,会经常出现各种活动,如限时秒杀,秒杀活动的并发量特别高,会导致访问变慢、商品超卖等问题。学会以下知识将会解决这些部分问题。
使用技术
开发工具:idea、navicat、RedisDesktopManager
开发环境:JDK1.8、MySql8.0、Maven、SpringBoot、redis、RabbitMQ
点击将会进入相关安装教程
创建表
1、order(订单)表:
2、commodity_inventory(商品库存)表
配置pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.8.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.0.3-beta1</version> </dependency> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>4.0.0</version> </dependency> </dependencies>
配置application.yml
spring: devtools: restart: enabled: false datasource: username: root password: root url: jdbc:mysql://localhost:3306/seconds_kill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: 123456 jedis: pool: max-active: 1024 max-wait: -1s max-idle: 200 rabbitmq: host: localhost port: 5672 username: guest password: guest server: port: 1224
创建实体类
1、order
import java.io.Serializable; public class Order implements Serializable { private Integer id; private String cname; private String userName; private Integer ciId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Integer getCiId() { return ciId; } public void setCiId(Integer ciId) { this.ciId = ciId; } }
2、CommodityInventory
import java.io.Serializable; public class CommodityInventory implements Serializable { private Integer id; private String cName; private Integer inventory; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getcName() { return cName; } public void setcName(String cName) { this.cName = cName; } public Integer getInventory() { return inventory; } public void setInventory(Integer inventory) { this.inventory = inventory; } }
Mapper
1、OrderMapper
import tk.mybatis.mapper.common.Mapper; import tk.mybatis.mapper.common.MySqlMapper; public interface GenericMapper<T> extends Mapper<T>, MySqlMapper<T> { }
2、CommodityInventoryMapper
import com.example.demo.base.GenericMapper; import com.example.demo.pojo.CommodityInventory; public interface CommodityInventoryMapper extends GenericMapper<CommodityInventory> { void insertCommodityInventory(CommodityInventory commodityInventory); }
base
import tk.mybatis.mapper.common.Mapper; import tk.mybatis.mapper.common.MySqlMapper; public interface GenericMapper<T> extends Mapper<T>, MySqlMapper<T> { }
消息队列
MyRabbitMQConfig:
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(); } }
Redis
RedisConfig:
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<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet(); return template; } }
开始秒杀
1、MQOrderService
import com.example.demo.config.MyRabbitMQConfig; import com.example.demo.pojo.Order; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class MQOrderService { private static Logger log = LoggerFactory.getLogger(MQOrderService.class); @Autowired private OrderService orderService; /** * 监听订单消息队列,并消费 * * @param order */ @RabbitListener(queues = MyRabbitMQConfig.ORDER_QUEUE) public void createOrder(Order order) { log.info("收到订单消息,订单用户为:{},商品名称为:{}", order.getUserName(), order.getCname()); /** * 调用数据库orderService创建订单信息 */ orderService.createOreder(order); } }
2、MQCommodityInventoryService
import com.example.demo.config.MyRabbitMQConfig; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @Slf4j public class MQCommodityInventoryService { private static Logger log = LoggerFactory.getLogger(MQStockService.class); @Autowired private CommodityInventoryService commodityInventoryService; /** * 监听库存消息队列,并消费 * @param stockName */ @RabbitListener(queues = MyRabbitMQConfig.STORY_QUEUE) public void decrByStock(String stockName) { log.info("库存消息队列收到的消息商品信息是:{}", stockName); /** * 调用数据库service给数据库对应商品库存减一 */ commodityInventoryService.decrByStock(stockName); } }
Service
1、CommodityInventoryServiceImpl
import com.example.demo.mapper.CommodityInventoryMapper; import com.example.demo.pojo.CommodityInventory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import tk.mybatis.mapper.entity.Example; import java.util.List; import com.example.demo.service.CommodityInventoryService; @Service public class CommodityInventoryServiceImpl implements CommodityInventoryService { @Autowired private CommodityInventoryMapper commodityInventoryMapper; // 秒杀商品后减少库存 @Override public void decrByStock(String cName) { Example example = new Example(CommodityInventory.class); Example.Criteria criteria = example.createCriteria(); criteria.andEqualTo("name", cName); List<CommodityInventory> stocks = commodityInventoryMapper.selectByExample(example); if (!CollectionUtils.isEmpty(stocks)) { CommodityInventory commodityInventory = stocks.get(0); commodityInventory.setInventory(commodityInventory.getInventory() - 1); commodityInventoryMapper.updateByPrimaryKey(commodityInventory); } } // 秒杀商品前判断是否有库存 @Override public Integer selectByExample(String cName) { Example example = new Example(CommodityInventory.class); Example.Criteria criteria = example.createCriteria(); criteria.andEqualTo("name", cName); List<CommodityInventory> commodityInventories = commodityInventoryMapper.selectByExample(example); if (!CollectionUtils.isEmpty(commodityInventories)) { return commodityInventories.get(0).getInventory().intValue(); } return 0; } }
2、OrderServiceImpl
import com.example.demo.mapper.OrderMapper; import com.example.demo.pojo.Order; import com.example.demo.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 createOreder(Order order) { orderMapper.insert(order); } }
Controller
SecController :
import com.example.demo.config.MyRabbitMQConfig; import com.example.demo.pojo.Order; import com.example.demo.service.CommodityInventoryService; import com.example.demo.service.MQCommodityInventoryService; import com.example.demo.service.OrderService; import com.example.demo.service.RedisService; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 { private static Logger log = LoggerFactory.getLogger(MQCommodityInventoryService.class); @Autowired private RabbitTemplate rabbitTemplate; @Autowired private RedisService redisService; @Autowired private OrderService orderService; @Autowired private CommodityInventoryService commodityInventoryService; /** * 使用redis+消息队列进行秒杀实现 * * @param username * @param cName * @return */ @PostMapping( value = "/sec",produces = "application/json;charset=utf-8") @ResponseBody public String sec(@RequestParam(value = "username") String username, @RequestParam(value = "cName") String cName) { log.info("参加秒杀的用户是:{},秒杀的商品是:{}", username, cName); String message = null; //调用redis给相应商品库存量减一 Long decrByResult = redisService.decrBy(cName); if (decrByResult >= 0) { /** * 说明该商品的库存量有剩余,可以进行下订单操作 */ log.info("用户:{}秒杀该商品:{}库存有余,可以进行下订单操作", username, cName); //发消息给库存消息队列,将库存数据减一 rabbitTemplate.convertAndSend(MyRabbitMQConfig.STORY_EXCHANGE, MyRabbitMQConfig.STORY_ROUTING_KEY, cName); //发消息给订单消息队列,创建订单 Order order = new Order(); order.setCname(cName); order.setUserName(username); rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE, MyRabbitMQConfig.ORDER_ROUTING_KEY, order); message = "用户" + username + "秒杀" + cName + "成功"; } else { /** * 说明该商品的库存量没有剩余,直接返回秒杀失败的消息给用户 */ log.info("用户:{}秒杀时商品的库存量没有剩余,秒杀结束", username); message = "用户:"+ username + "商品的库存量没有剩余,秒杀结束"; } return message; } }
多条访问http://localhost:1224/sec?username&cName
可看到控制台的日志打印
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- #使用idea创建springboot +Redis +RabbitMQ 实现高并发限时秒杀
- 入门基础必备,使用Idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter模拟实现高并发秒杀
- 使用idea实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter的高并发秒杀
- SpringBoot +Redis +RabbitMQ 实现高并发限时秒杀
- 新手入门,Springboot+redis+rabbitmq+Jmeter实现高并发现时秒杀,idea+maven测试
- idea基础实现SpringBoot+Mysql+Redis+RabbitMQ+Jmeter模拟高并发秒杀
- 实现简单秒杀抢购,使用SpringBoot整合Spring-data-redis 、 rabbitMQ消息队列、redis缓存
- springboot 整合 redis 主从同步 sentinel哨兵 实现商品抢购秒杀
- Springboot+redis实现商品秒杀
- 使用springboot+redis实现session共享
- 使用springboot+redis实现session共享
- spring boot Rabbitmq集成,延时消息队列实现
- springboot中使用redis实现异步消息通知
- spring boot Rabbitmq集成,延时消息队列实现
- 详细介绍Spring Boot + RabbitMQ实现延迟队列
- Spring boot 使用Redis实现缓存功能
- SpringBoot RabbitMQ 整合使用
- springboot+dubbo+zookeeper+mybatis+redis+druid+rabbitmq
- Spring Boot系列(七)Spring Boot使用Redis实现session共享
- 基于dubbo的分布式项目框架搭建 开发工具idea (springboot+dubbo+zookeeper+redis+rabbitmq+基于Swagger2的restful api) --(一)