谷粒商城RabbitMQ锁库存逻辑详解--新理解(长文警告)

谷粒商城RabbitMQ锁库存逻辑详解--新理解(长文警告),第1张

谷粒商城RabbitMQ锁库存逻辑详解--新理解(长文警告) 前言

不废话,上来就说,代码我会放挺多,写过这个项目的自然能懂,如果真的像理解的请认真看哦

分析
                
                R r = wmsFeignService.orderLockStock(lockVo);
                if (r.getCode() == 0) {
                    //锁定成功
                    responseVo.setOrder(order.getOrder());
                    int i = 10/0;//注意这一行!!!!!!!!!!!!!!!!!!!!!!

                    //TODO 订单创建成功,发送消息给MQ
                    rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order.getOrder());

                    //删除购物车里的数据
                    redisTemplate.delete(CART_PREFIX+memberResponseVo.getId());
                    return responseVo;
                } else {
                    //锁定失败
                    String msg = (String) r.get("msg");
                    throw new NoStockException(msg);
                    // responseVo.setCode(3);
                    // return responseVo;
                }

上面的代码是提交订单那里的,请仔细看上面的逻辑,首先先远程调用wmsFeignService.orderLockStock(lockVo),接下来让我们看看这个方法

@PostMapping(value = "/lock/order")
    public R orderLockStock(@RequestBody WareSkuLockVo vo) {
        try {
            boolean lockStock = wareSkuService.orderLockStock(vo);
            return R.ok().setData(lockStock);
        } catch (NoStockException e) {
            return R.error(NO_STOCK_EXCEPTION.getCode(),NO_STOCK_EXCEPTION.getMessage());
        }
    }

这是它的controller,它通过检测下面的service方法看有没有异常,有异常就return R.error
没有就return R.ok

	 if (org.springframework.util.StringUtils.isEmpty(wareIds)) {
	     //没有任何仓库有这个商品的库存
	     throw new NoStockException(skuId);
	 }
	
	 //1、如果每一个商品都锁定成功,将当前商品锁定了几件的工作单记录发给MQ
	 //2、锁定失败。前面保存的工作单信息都回滚了。发送出去的消息,即使要解锁库存,由于在数据库查不到指定的id,所有就不用解锁
	 for (Long wareId : wareIds) {
	     //锁定成功就返回1,失败就返回0
	     Long count = wareSkuDao.lockSkuStock(skuId,wareId,hasStock.getNum());
	     if (count == 1) {
	         skuStocked = true;
	         WareOrderTaskDetailEntity taskDetailEntity = WareOrderTaskDetailEntity.builder()
	                 .skuId(skuId)
	                 .skuName("")
	                 .skuNum(hasStock.getNum())
	                 .taskId(wareOrderTaskEntity.getId())
	                 .wareId(wareId)
	                 .lockStatus(1)
	                 .build();
	         wareOrderTaskDetailService.save(taskDetailEntity);
	
	         //TODO 告诉MQ库存锁定成功
	         StockLockedTo lockedTo = new StockLockedTo();
	         lockedTo.setId(wareOrderTaskEntity.getId());
	         StockDetailTo detailTo = new StockDetailTo();
	         BeanUtils.copyProperties(taskDetailEntity,detailTo);
	         lockedTo.setDetailTo(detailTo);
	         rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",lockedTo);
	         break;
	     } else {
	         //当前仓库锁失败,重试下一个仓库
	     }
	 }
	
	 if (skuStocked == false) {
	     //当前商品所有仓库都没有锁住
	     throw new NoStockException(skuId);
	 }
首先请注意异常抛出的地方

没有任何仓库有这个商品的库存,当前商品所有仓库都没有锁住,才会抛出异常!而抛出异常意味着,提交订单(请看第一个代码块)那边的 if (r.getCode() == 0) 这个判断绝对会判断失败,从而走else逻辑,此时说明库存根本没锁到(因为异常就是因为没锁到或没库存才抛出),所以根本不需要额外写一个逻辑去判断库存需不需要解锁,没锁还解锁啥呀。

其次请注意锁成功的话会发生什么

锁成功就会向消息队列发送“这个商品已经被锁上了”的消息,延迟时间50min(在视频里老师设置了2min以便观察现象),请记住这个锁仓库成功的 *** 作。此时 if (r.getCode() == 0) 这个判断绝对为真,于是进入下面的逻辑

请注意,既然进入这个逻辑,说明锁库存没抛异常,说明锁成功了,那这里的 int i = 10/0 会导致这个方法出现异常。在没有加入seata的时候,这整个方法只有非远程方法可以回滚,加入seata后,在入口方法加入@GlobalTransactional,在从属方法下加入@Transactional,可以做到全局回滚。
但是老师最后不用这个方法,他用了我之前发的一篇文章:
谷粒商城RabbitMQ设计思想详解:消息队列双重保险设计
这种方法来实现解锁库存的 *** 作,因为如果用seata会导致吞吐量下降严重。

下面我将描述解锁库存为什么不需要自己手动做

25号有个同学私信我说,在上面那张图的else部分,无论成功还是失败都往消息队列发送消息,让他判断要不要解锁库存。我觉得可能是没搞懂设计逻辑。
首先我们必须明确,解锁库存是在哪做的,在什么时候做的?
是在submitOrder这个方法完整执行后,用消息队列监听两个死信队列做的。
我怕大家忘记老师的设计模式,我再强调一次,老师实现的是最终一致性。
我给大家放一个图片

你看,这么多分支情况,最终都会进入一个判断“解不解锁”的逻辑,大家应该联系整个系统,在所有逻辑走到头的情况下再个性化地添加不同的解锁逻辑,如果像私信我的那个同学的解锁,放在else块里面,我觉得那个耦合度,应该有点大,而且很不方便维护,我是这样觉得的哦

如果上面的图片不够清晰,那你可以试试下载这个
思维导图…111
我不知道清晰度是不是一样的…
我迫不得已才搞了个思维导图,能想到的基本写出来了,然后你如果做过项目,你思考一下,会发现老师基本把百分之90的情况搞定了,也就是大部分地方报错,库存那边都能做到严密的自解锁,可能中途有点一致性错误,不过既然是追求最终一致性,所以没什么所谓。

尾声

本次要分享的就是这些,我自认为写的还算详细,如果说错了什么,或者有什么要讨论的,大可以评论或者私信我,可以一起想哦

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5687805.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存