6步带你用Spring Boot开发出商城高并发秒杀系统 |
发表者:admin分类:应用服务2023-04-10 11:51:44 阅读[388] |
6步带你用Spring Boot开发出商城高并发秒杀系统
本博客将介绍如何使用 Spring Boot 实现一个简单的商城秒杀系统,并通过使用 Redis 和 MySQL 来增强其性能和可靠性。
摘要:本博客将介绍如何使用 Spring Boot 实现一个简单的商城秒杀系统,并通过使用 Redis 和 MySQL 来增强其性能和可靠性。
准备工作
- JDK 1.8 或更高版本
- Redis
- MySQL
- MyBatis
实现步骤
步骤一:创建数据库
CREATE TABLE `goods` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
`name` varchar(50) NOT NULL COMMENT '商品名称',
`description` varchar(100) NOT NULL COMMENT '商品描述',
`price` decimal(10,2) NOT NULL COMMENT '商品价格',
`stock_count` int(11) NOT NULL COMMENT '商品库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
CREATE TABLE `order` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`user_id` BIGINT(20) NOT NULL COMMENT '用户ID',
`goods_id` BIGINT(20) NOT NULL COMMENT '商品ID',
`seckill_id` BIGINT(20) DEFAULT NULL COMMENT '秒杀活动ID',
`status` TINYINT(4) NOT NULL COMMENT '订单状态,0-未支付,1-已支付,2-已发货,3-已收货,4-已退款,5-已完成',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_order` (`user_id`,`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
CREATE TABLE `seckill` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '秒杀活动ID',
`goods_id` BIGINT(20) NOT NULL COMMENT '商品ID',
`start_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间',
`end_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '结束时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='秒杀活动表';
步骤二:创建 Spring Boot 项目
步骤三:配置 Redis 和 MySQL
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shop?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456
步骤四:编写实体类和 DAO 接口
// 商品实体类
@Data
public class Goods {
private Long id;
private String name;
private String description;
private BigDecimal price;
private Integer stockCount;
}
// 商品 DAO 接口
@Mapper
public interface GoodsDao {
@Select("SELECT * FROM goods WHERE id = #{id}")
Goods getGoodsById(Long id);
@Update("UPDATE goods SET stock_count = stock_count - 1 WHERE id = #{id} AND stock_count > 0")
int reduceStockCount(Long id);
}
// 订单实体类
@Data
public class Order {
private Long id;
private Long userId;
private Long goodsId;
private Long seckillId;
private Byte status;
private Date createTime;
private Date updateTime;
}
// 订单 DAO 接口
@Mapper
public interface OrderDao {
@Select("SELECT * FROM `order` WHERE user_id = #{userId} AND goods_id = #{goodsId}")
Order getOrderByUserIdAndGoodsId(@Param("userId") Long userId, @Param("goodsId") Long goodsId);
@Insert("INSERT INTO `order` (user_id, goods_id, seckill_id, status, create_time, update_time) VALUES (#{userId}, #{goodsId}, #{seckillId}, #{status},#{createTime},#{updateTime})")
int insertOrder(Order order);
@Select("SELECT o.*, g.name, g.price FROM `order` o LEFT JOIN goods g ON o.goods_id = g.id WHERE o.user_id = #{userId}")
List<OrderVo> getOrderListByUserId(Long userId);
}
// 秒杀活动实体类
@Data
public class Seckill {
private Long id;
private Long goodsId;
private Date startTime;
private Date endTime;
}
// 秒杀活动 DAO 接口
@Mapper
public interface SeckillDao {
@Select("SELECT * FROM seckill WHERE id = #{id}")
Seckill getSeckillById(Long id);
@Update("UPDATE seckill SET end_time = #{endTime} WHERE id = #{id}")
int updateSeckillEndTime(@Param("id") Long id, @Param("endTime") Date endTime);
}
步骤五:编写 Service 层和 Controller
- 商品 Service 层:用于获取商品信息和减少商品库存数量。
@Service
public class GoodsService {
private final GoodsDao goodsDao;
@Autowired
public GoodsService(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
public Goods getGoodsById(Long id) {
return goodsDao.getGoodsById(id);
}
public boolean reduceStockCount(Long id) {
return goodsDao.reduceStockCount(id) > 0;
}
}
- 订单 Service 层:用于创建订单和获取订单信息。
@Service
public class OrderService {
private final OrderDao orderDao;
@Autowired
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public Order createOrder(Long userId, Long goodsId, Long seckillId) {
Order order = new Order();
order.setUserId(userId);
order.setGoodsId(goodsId);
order.setSeckillId(seckillId);
order.setStatus((byte) 0);
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
orderDao.insertOrder(order);
return order;
}
public List<OrderVo> getOrderListByUserId(Long userId) {
return orderDao.getOrderListByUserId(userId);
}
}
- 秒杀活动 Service 层:用于获取秒杀活动信息和更新秒杀活动结束时间。
@Service
public class SeckillService {
private final SeckillDao seckillDao;
@Autowired
public SeckillService(SeckillDao seckillDao) {
this.seckillDao = seckillDao;
}
public Seckill getSeckillById(Long id) {
return seckillDao.getSeckillById(id);
}
public boolean updateSeckillEndTime(Long id, Date endTime) {
return seckillDao.updateSeckillEndTime(id, endTime) > 0;
}
}
- 订单 Controller:用于处理订单相关的请求。
@RestController
@RequestMapping("/order")
public class OrderController {
private final OrderService orderService;
@Autowired
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping("/create")
public CommonResult<Order> createOrder(@RequestParam("userId") Long userId,
@RequestParam("goodsId") Long goodsId,
@RequestParam("seckillId") Long seckillId) {
Order order = orderService.createOrder(userId, goodsId, seckillId);
if (order == null) {
return CommonResult.failed(ResultCode.FAILURE);
}
return CommonResult.success(order);
}
@GetMapping("/list")
public CommonResult<List<OrderVo>> getOrderListByUserId(@RequestParam("userId") Long userId) {
List<OrderVo> orderList = orderService.getOrderListByUserId(userId);
return CommonResult.success(orderList);
}
}
@RestController
@RequestMapping("/seckill")
public class SeckillController {
private final SeckillService seckillService;
private final GoodsService goodsService;
private final OrderService orderService;
@Autowired
public SeckillController(SeckillService seckillService, GoodsService goodsService, OrderService orderService) {
this.seckillService = seckillService;
this.goodsService = goodsService;
this.orderService = orderService;
}
@PostMapping("/start")
public CommonResult<Object> startSeckill(@RequestParam("userId") Long userId,
@RequestParam("goodsId") Long goodsId,
@RequestParam("seckillId") Long seckillId) {
// 查询秒杀活动是否有效
Seckill seckill = seckillService.getSeckillById(seckillId);
if (seckill == null || seckill.getStartTime().after(new Date()) || seckill.getEndTime().before(new Date())) {
return CommonResult.failed(ResultCode.FAILURE, "秒杀活动不存在或已结束");
}
// 判断商品库存是否充足
Goods goods = goodsService.getGoodsById(goodsId);
if (goods == null || goods.getStockCount() <= 0) {
return CommonResult.failed(ResultCode.FAILURE, "商品库存不足");
}
// 生成订单
Order order = orderService.createOrder(userId, goodsId, seckillId);
if (order == null) {
return CommonResult.failed(ResultCode.FAILURE, "订单创建失败,请稍后再试");
}
// 减少商品库存
boolean success = goodsService.reduceStockCount(goodsId);
if (!success) {
return CommonResult.failed(ResultCode.FAILURE, "减少商品库存失败,请稍后再试");
}
return CommonResult.success("秒杀成功");
}
}
步骤六:使用 Redis 实现分布式锁
@Service
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public RedisService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public boolean lock(String key, String value, long expire) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofSeconds(expire));
return result != null && result;
}
public void unlock(String key, String value) {
if (value.equals(redisTemplate.opsForValue().get(key))) {
redisTemplate.delete(key);
}
}
}
@Service
public class SeckillService {
private final RedisService redisService;
private final SeckillDao seckillDao;
private final GoodsDao goodsDao;
private final OrderDao orderDao;
@Autowired
public SeckillService(RedisService redisService, SeckillDao seckillDao, GoodsDao goodsDao, OrderDao orderDao) {
this.redisService = redisService;
this.seckillDao = seckillDao;
this.goodsDao = goodsDao;
this.orderDao = orderDao;
}
public CommonResult<Object> startSeckill(Long userId, Long goodsId, Long seckillId) {
String lockKey = "seckill:lock:" + goodsId;
String lockValue = UUID.randomUUID().toString();
try {
// 获取分布式锁
if (!redisService.lock(lockKey, lockValue, 10)) {
return CommonResult.failed(ResultCode.FAILURE, "当前请求太过频繁,请稍后再试");
}
// 查询秒杀活动是否有效
Seckill seckill = seckillDao.getSeckillById(seckillId);
if (seckill == null || seckill.getStartTime().after(new Date()) || seckill.getEndTime().before(new Date())) {
return CommonResult.failed(ResultCode.FAILURE, "秒杀活动不存在或已结束");
}
// 判断商品库存是否充足
Goods goods = goodsDao.getGoodsById(goodsId);
if (goods == null || goods.getStockCount() <= 0) {
return CommonResult.failed(ResultCode.FAILURE, "商品库存不足");
}
// 创建订单
Order order = new Order();
order.setUserId(userId);
order.setGoodsId(goodsId);
order.setSeckillId(seckillId);
order.setStatus((byte) 0);
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
int count = orderDao.insertOrder(order);
if (count <= 0) {
return CommonResult.failed(ResultCode.FAILURE, "订单创建失败,请稍后再试");
}
// 减少商品库存
boolean success = goodsDao.reduceStockCount(goodsId) > 0;
if (!success) {
throw new Exception("减少商品库存失败,请稍后再试");
}
return CommonResult.success("秒杀成功");
} catch (Exception e) {
e.printStackTrace();
return CommonResult.failed(ResultCode.FAILURE, "秒杀失败," + e.getMessage());
} finally {
// 释放分布式锁
redisService.unlock(lockKey, lockValue);
}
}
}
转载请标明出处【6步带你用Spring Boot开发出商城高并发秒杀系统】。
《www.micoder.cc》
虚拟化云计算,系统运维,安全技术服务.
Tags: | [阅读全文...] |
最新评论