业务分析
一般而言,商品秒杀大概可以拆分成以下几步:
- 用户校验
校验是否多次抢单,保证每个商品每个用户只能秒杀一次 - 下单
订单信息进入消息队列,等待消费 - 减少库存
消费订单消息,减少商品库存,增加订单记录 - 付款
十五分钟内完成支付,修改支付状态
创建表
goods_info 商品库存表
列 | 说明 |
---|---|
id | 主键(uuid) |
goods_name | 商品名称 |
goods_stock | 商品库存 |
package com.jason.seckill.order.entity;
/**
* 商品库存
*/
public class GoodsInfo {
private String id;
private String goodsName;
private String goodsStock;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public String getGoodsStock() {
return goodsStock;
}
public void setGoodsStock(String goodsStock) {
this.goodsStock = goodsStock;
}
@Override
public String toString() {
return "GoodsInfo{" +
"id='" + id + '\'' +
", goodsName='" + goodsName + '\'' +
", goodsStock='" + goodsStock + '\'' +
'}';
}
}
order_info 订单记录表
列 | 说明 |
---|---|
id | 主键(uuid) |
user_id | 用户id |
goods_id | 商品id |
pay_status | 支付状态(0-超时未支付 1-已支付 2-待支付) |
package com.jason.seckill.order.entity;
/**
* 下单记录
*/
public class OrderRecord {
private String id;
private String userId;
private String goodsId;
/**
* 0-超时未支付 1-已支付 2-待支付
*/
private Integer payStatus;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getGoodsId() {
return goodsId;
}
public void setGoodsId(String goodsId) {
this.goodsId = goodsId;
}
public Integer getPayStatus() {
return payStatus;
}
public void setPayStatus(Integer payStatus) {
this.payStatus = payStatus;
}
@Override
public String toString() {
return "OrderRecord{" +
"id='" + id + '\'' +
", userId='" + userId + '\'' +
", goodsId='" + goodsId + '\'' +
'}';
}
}
功能实现
1.用户校验
使用redis做用户校验,保证每个用户每个商品只能抢一次,上代码:
public boolean checkSeckillUser(OrderRequest order) {
String key = env.getProperty("seckill.redis.key.prefix") + order.getUserId() + order.getGoodsId();
return redisTemplate.opsForValue().setIfAbsent(key,"1");
}
userId+orderId的组合作为key,利用redis的setnx分布式锁原理来实现。如果是限时秒杀,可以通过设置key的过期时间来实现。
2.下单
下单信息肯定是要先扔到消息队列里的,这里采用RabbitMQ来做消息队列,先来看一下消息队列的模型图:
rabbitmq的配置:
#rabbitmq配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#消费者数量
spring.rabbitmq.listener.simple.concurrency=5
#最大消费者数量
spring.rabbitmq.listener.simple.max-concurrency=10
#消费者每次从队列获取的消息数量。写多了,如果长时间得不到消费,数据就一直得不到处理
spring.rabbitmq.listener.simple.prefetch=1
#消费接收确认机制-手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
mq.env=local
#订单处理队列
#交换机名称
order.mq.exchange