分布式锁应用(预下单)
- controller
//添加分布式锁
@PostMapping("create_order2")
public AjaxResult createOrder2(@RequestBody CreateOrderReq req) {
String lockKey = req.getId();//锁(业务id)
String threadId = IDGeneratorUtils.getUUID().toString();//线程唯一标识
Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey,threadId, 10, TimeUnit.SECONDS);
if(!flag) {
return AjaxResult.error("当前系统繁忙,请稍后再试");
}
AjaxResult order = null;
try {
order = skuOrderService.createOrder(req);
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
} finally {
//在释放锁时候,保证是当前线程id加的锁
if(threadId.equals(redisTemplate.opsForValue().get(lockKey))) {
//假如这个卡顿一下,可能会删除 下一个线程2加的锁
//释放当前业务完成后释放锁
redisTemplate.delete(lockKey);
}
}
return order;
}
- service
@Override
@Transactional(rollbackFor = Exception.class)
public AjaxResult createOrder(CreateOrderReq req) throws CustomException {
//校验库存是否为0
Sku sku = skuMapper.selectOne(Wrappers.<Sku>lambdaQuery().eq(StringUtils.isBlank(req.getId()), Sku::getId, req.getId()));
if(!Objects.isNull(sku)) {
// shopPoolWater.getBusinessNum() > shopPool.getStock() - shopPool.getFrozenNum()
if(req.getTradeNum() > sku.getStock() - sku.getFrozenNum()) {
return AjaxResult.error("库存不足");
}
}else {
return AjaxResult.error("查询当前商品为null");
}
//冻结库存
skuMapper.addFrozenNum(req.getId(),req.getTradeNum());
AtomicInteger atomicInteger = new AtomicInteger();
int andIncrement = atomicInteger.getAndIncrement();
System.out.println(atomicInteger.get());
//创建订单
SkuOrder skuOrder = SkuOrder.builder()
.id(IdWorker.getId()).skuId(req.getId()).orderCode(OrderUtil.generateOrderNo("cs")).status("4").tradeNum(req.getTradeNum()).uid(req.getUid())
.build();
SkuOrderServiceImpl skuOrderService =(SkuOrderServiceImpl) AopContext.currentProxy();
boolean save = false;
try {
save = skuOrderService.save(skuOrder);
} catch (Exception e) {
e.printStackTrace();
throw new CustomException(e.getMessage(), 500);
}
if(save){
//减少冻结数量,减少库存,增加领取数量
skuMapper.updateStock(req.getId(), req.getTradeNum());
Sku sku2 = skuMapper.selectOne(Wrappers.<Sku>lambdaQuery().eq(StringUtils.isBlank(req.getId()), Sku::getId, req.getId()));
Integer stock = sku2.getStock();
return AjaxResult.success("下单和更新库存成功,剩余库存:" + stock);
}else {
return AjaxResult.error("下单和更新库存失败");
}
}
- mapper maybatis自带的
public interface SkuMapper extends BaseMapper<Sku> {
/**
* 增加冻结数量
* */
@Update({"update test_product set frozen_num = frozen_num + #{tradeNum} where id = #{id}"})
void addFrozenNum(@Param("id") String id, @Param("tradeNum") Integer tradeNum);
/**
* 更新库存
* */
@Update({"update test_product set stock = stock - #{tradeNum}, receive_num = receive_num + #{tradeNum}, frozen_num = frozen_num - #{tradeNum} where stock >= #{tradeNum} and frozen_num >= #{tradeNum} and id = #{id}"})
void updateStock(@Param("id") String id, @Param("tradeNum") Integer tradeNum);
}
- domain
@Data
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("test_product_order")
@Builder(toBuilder = true)
public class SkuOrder implements Serializable {
private static final long serialVersionUID=1L;
@TableId(value = "id", type= IdType.INPUT)
private Long id;
@TableField(value = "sku_id")
private String skuId;
@TableField(value = "order_code")
private String orderCode;
@TableField(value = "trade_num")
private Integer tradeNum;
@TableField(value = "status")
private String status;
@TableField(value = "uid")
private String uid;
}
```@Data
@ToString
@EqualsAndHashCode(callSuper = false)
@TableName("test_product")
public class Sku implements Serializable {
private static final long serialVersionUID=1L;
@TableId(value = "id", type= IdType.INPUT)
private Long id;
@TableField(value = "sku_name")
private String skuName;
@TableField(value = "stock")
private Integer stock;
@TableField(value = "init_num")
private Integer initNum;
@TableField(value = "receive_num")
private Integer receiveNum;
@TableField(value = "frozen_num")
private Integer frozenNum;
}
- 使用jmeter 测试 高并发 2000个 结果
微信图片_20210830103154.png
微信图片_20210830103203.png
bug - 通过图上,当1秒内2000次并发,库存没有变变成负数,但是下单数量多了19条记录
咋样解决?
敬请期待
网友评论