目前设想的大致的序列图
秒杀开始前,初始化数据库秒杀信息,并同步到redis缓存中,秒杀开始后,用户直接访问redis缓存进行库存扣减,当剩余库存小于0时说明商品抢购完毕,直接返回库存不足抢购失败,抢购成功的用户返回"秒杀成功,订单处理中,请稍后查看",并且成功的抢购信息进入队列,异步扣减数据库实际库存并下单.用户查询订单,根据用户和商品查询对应的订单信息返回给用户.
①.、减订单sql:
准备
JMeter:用于模拟多线程用户秒杀
ActiveMQ:消息队列
redis:缓存
mysql:数据库
环境搭建(前面已经介绍了springboot◆mybatis◆druid◆activemq◆redis的整合)
mysql创建表? tproduct-商品? torder-订单
启动redis和activemq
pom.xml
xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/④0.0" xmlns:xsi="http://www.w③org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/④0.0 http://maven.apache.org/xsd/maven-④0.0.xsd"> <modelVersion>④0.0modelVersion> <parent> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-parentartifactId> <version>2.1.④RELEASEversion> <relativePath/> parent> <groupId>com.examplegroupId> <artifactId>test2artifactId> <version>0.0.1-SNAPSHOTversion> <name>test2name> <description>Demo project for Spring Bootdescription> <properties> <java.version>1.8java.version> properties> <dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-activemqartifactId> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> <version>⑤1.38version> dependency> <dependency> <groupId>org.mybatis.spring.bootgroupId> <artifactId>mybatis-spring-boot-starterartifactId> <version>1.③2version> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>druid-spring-boot-starterartifactId> <version>1.1.10version> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-redisartifactId> dependency> dependencies> <build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> plugins> build> project>
application.properties
#tomcat 配置 默认8080 可以改成其他端口,这里显式配置 server.port=8080 #activemq 配置 用户名密码 用默认值 spring.activemq.broker-url=tcp://0.0.0.0:61616 #spring.jms.template.default-destination=test-queue spring.jms.template.default-destination=flash-queue #druid数据源 spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.url=jdbc:mysql://192.16⑧1.104:3306/test?useUnicode=truecharacterEncoding=utf-8 spring.datasource.druid.username=root spring.datasource.druid.password=root #数据库连接池配置 spring.datasource.druid.initial-size=5 spring.datasource.druid.max-active=20 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-wait=30000 #mybatis mybatis.mapper-locations=classpath:mapper/*.xml #mybatis.type-aliases-package=com.flysand.demo.entity #redis配置 # Redis数据库索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=192.16⑧1.113 # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password=test123 # 连接池最大连接数(使用负值表示没有限制) # spring boot 1版本配置 #spring.redis.pool.max-active=8 #spring boot 2 版本配置 spring.redis.jedis.pool.max-active=10 # 连接池最大阻塞等待时间(使用负值表示没有限制) #spring.redis.pool.max-wait=-1ms spring.redis.jedis.pool.max-wait=-1 # 连接池中的最大空闲连接 #spring.redis.pool.max-idle=8 spring.redis.jedis.pool.max-idle=8 # 连接池中的最小空闲连接 #spring.redis.pool.min-idle=0 spring.redis.jedis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=2000ms #默认logback日志配置 #日志文件配置 path为空,则在项目根目录 file为空,则默认为spring.log logging.path= logging.file=test2.log #日志级别 root级别 logging.level.root=info #自定义包日志级别 logging.level.com.flysand=debug #格式 - 控制台 logging.pattern.console=[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] - [%-5level][%logger{50}:%line] - %msg%n #文件 日期默认格式yyyy-MM-dd HH:mm:ss.SSS logging.pattern.file=[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] - [%-5level][%logger{50}:%line] - %msg%n
ProductServiceImpl.java
package com.flysand.demo.service.impl; import com.flysand.demo.activemq.ActiveMqProducer; import com.flysand.demo.dao.TProductMapper; import com.flysand.demo.entity.TProduct; import com.flysand.demo.service.ProductService; import com.flysand.demo.util.RedisUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author flysand on 2019/04/18 **/ @Service public class ProductServiceImpl implements ProductService { private static final Logger logger = LoggerFactory.getLogger(ProductServiceImpl.class); @Autowired private TProductMapper tProductMapper; @Autowired private RedisUtils redisUtils; @Autowired private ActiveMqProducer producer; @Override public int createProduct(TProduct product) { return tProductMapper.insert(product); } @Override public int decreaseProduct(String productId) { return tProductMapper.decreaseById(productId); } @Override public int getStockById(String productId) { return tProductMapper.selectProductStock(productId); } @Override public TProduct getProductById(String productId) { return tProductMapper.selectById(productId); } @Override public String syncStock(String productId) { String result = "同步库存成功"; try { int count = tProductMapper.selectProductStock(productId); redisUtils.setString(productId, String.valueOf(count)); } catch (Exception e) { logger.error("同步库存异常:{}", e.getMessage()); result = "同步库存异常"; } return result; } @Override public String flash(String key) { String result = "抢购提交成功,订单处理中"; // 原子减1 long count = (long) redisUtils.increase(key, -1L); String name = Thread.currentThread().getName(); // 把redis减1后还大于0即还有库存的设为抢购成功,并放入成功队列 if (count >= 0) { result ◆= ",抢购线程" ◆ name ◆ ",抢购1个,剩余" ◆ count; //redisUtils.sset("success", name); // 推送队列 String msg = key ◆ "," ◆ name; producer.sendMessage(msg); } else { result = "库存不足,抢购失败" ◆ name; // redisUtils.sset("fail", name); } return result; } }
ActiveMqConsumer.java
package com.flysand.demo.activemq; import com.flysand.demo.entity.TOrder; import com.flysand.demo.entity.TProduct; import com.flysand.demo.service.OrderService; import com.flysand.demo.service.ProductService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.util.Date; import java.util.Random; /** * @author flysand on 2019/04/11 **/ @Component public class ActiveMqConsumer { private static final Logger logger = LoggerFactory.getLogger(ActiveMqConsumer.class); @Autowired private ProductService productService; @Autowired private OrderService orderService; @JmsListener(destination = "test-queue") public void receiveMessage(String text) { System.out.println("消费消息:" ◆ text); } @JmsListener(destination = "flash-queue") public void invokeFlash(String text) { logger.debug("执行秒杀后的下单操作"); String productId = text.split(",")[0]; String threadName = text.split(",")[1]; // 查询当前商品库存 TProduct product = productService.getProductById(productId); if (product == null || product.getpCount() <= 0) { logger.error("商品不存在或库存不足"); } else { // 减少库存,并下单 productService.decreaseProduct(productId); TOrder order = new TOrder(); long time = System.currentTimeMillis(); order.setOrderNo("P_" ◆ time); order.setProductId(productId); order.setQuantity(BigDecimal.ONE); order.setTotalAmount(product.getUnitPrice().multiply(BigDecimal.ONE)); order.setThreadName(threadName); orderService.createOrder(order); } } }
测试
后续增加多商品,以及下单信息回传队列,增加异常订单推送,以及websocket直接响应结果给前台.
git?源码:https://github.com/symflysand/secendsKill
以上就是土嘎嘎小编为大家整理的SpringBoot◆redis◆activemq秒杀场景简单整理相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!