不废话,上来就说,代码我会放挺多,写过这个项目的自然能懂,如果真的像理解的请认真看哦
分析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的情况搞定了,也就是大部分地方报错,库存那边都能做到严密的自解锁,可能中途有点一致性错误,不过既然是追求最终一致性,所以没什么所谓。
本次要分享的就是这些,我自认为写的还算详细,如果说错了什么,或者有什么要讨论的,大可以评论或者私信我,可以一起想哦
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)