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

前言

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

分析

                /*出现的问题:扣减库存成功了,但是由于网络原因超时,出现异常,导致订单事务回滚,库存事务不回滚(解决方案:seata)为了保证高并发,不推荐使用seata,因为是加锁,并行化,提升不了效率,可以发消息给库存服务*/R r = wmsFeignService.orderLockStock(lockVo);if (r.getCode() == 0) {//锁定成功responseVo.setOrder(order.getOrder());int i = 10/0;//注意这一行!!!!!!!!!!!!!!!!!!!!!!//TODO 订单创建成功,发送消息给MQrabbitTemplate.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,失败就返回0Long 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的情况搞定了,也就是大部分地方报错,库存那边都能做到严密的自解锁,可能中途有点一致性错误,不过既然是追求最终一致性,所以没什么所谓。

尾声

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NIO学习–核心概念与基本读写

转载自 NIO学习–核心概念与基本读写这两天花了时间学习了java的nio,看的书是Ron Hitchens著的 《Java NIO》,总的来说,这本书真的写的非常好,而且整本书将java nio的内容从底层讲了个遍,书不厚,但是确实值…

python3安装mysql模块_Python安装MySQL库详解,步骤及错误的解决方法

前面我们介绍的Python网络爬虫通常将抓取的数据存储至TXT或CSV文件,而当数据量增加之时,就需要将其存储至本地数据库了。Python访问数据库需要对应的接口程序,我们可以把接口程序理解为Python的一个模块,它提供了数据库客户端的接…

centos8安装docker

【README】本文参考了 docker官方文档安装指南, Install Docker Engine on CentOS | Docker DocumentationInstructions for installing Docker Engine on CentOShttps://docs.docker.com/engine/install/centos/ 【1】安装前的工作 1.需要centos7或8上&#xff1b…

34.在排序数组中查找元素的第一个和最后一个位置--leetcode算法题解(带注释)

public int[] searchRange(int[] nums, int target) {//先决条件排除一部分if(target < nums[0] || target > nums[nums.length - 1]){return new int[]{-1,-1};}//初始化左右边界int l 0;int r nums.length - 1;//初始化数组int[] arr {-1,-1};int mid 0;//代表左边…

datagridview绑定数据源不显示_sharding-jdbc系列之 数据源配置(一)

spring boot Yaml方式Bean定义一个Config类&#xff0c;配置数据源&#xff0c;上面的代码很简单&#xff0c;无非就是获取yaml文件&#xff0c;然后通过YmlByteArrayDataSource创建一个dataSource public YmlByteArrayDataSource继承了ShardingDataSource&#xff0c;调用了su…

关于 NIO 你不得不知道的一些“地雷”

转载自 关于 NIO 你不得不知道的一些“地雷”本文是笔者在学习NIO过程中发现的一些比较容易让人忽略的知识的一个总结&#xff0c;而这些让人忽略的小细节恰恰是NIO网络编程中必不可少。虽然现在我们不会直接编写NIO来完成我们的网络层通讯&#xff0c;而是使用成熟的基于NIO的…

转:Centos防火墙设置与端口开放的方法

转自&#xff1a; Centos防火墙设置与端口开放的方法_tianxin的专栏-CSDN博客Centos升级到7之后&#xff0c;内置的防火墙已经从iptables变成了firewalld。所以&#xff0c;端口的开启还是要从两种情况来说明的&#xff0c;即iptables和firewalld。更多关于CentOs防火墙的最新…

583. 两个字符串的删除操作用时6ms的另类解法

开门见山 看见这道题&#xff0c;我的第一反应不是去找出符合这道题的动态规划递推公式&#xff0c;我反而认为可以借用一下1143. 最长公共子序列的题解 class Solution {public int longestCommonSubsequence(String text1, String text2) {int[][] dp new int[text1.lengt…

Java 非阻塞 IO 和异步 IO

转载自 Java 非阻塞 IO 和异步 IO上一篇文章介绍了 Java NIO 中 Buffer、Channel 和 Selector 的基本操作&#xff0c;主要是一些接口操作&#xff0c;比较简单。 本文将介绍非阻塞 IO 和异步 IO&#xff0c;也就是大家耳熟能详的 NIO 和 AIO。很多初学者可能分不清楚异步和非阻…

element js 包含字符_selenium3.x(10)js弹框处理

web应用中&#xff0c;经常会遇到弹框。不处理弹框&#xff0c;页面其他元素都是不能操作的。js弹框有3种&#xff1a;alert警告框、confirm确认窗口、prompt信息输入窗口。webdriver提供了处理这3种弹框的方法。首先通过switch_to定位到弹框&#xff0c;然后针对弹框的不同&am…

转:centos8开启防火墙端口

转自&#xff1a; Centos8开放防火墙端口_Programmer-Awei的博客-CSDN博客查看防火墙某个端口是否开放firewall-cmd --query-port3306/tcp开放防火墙端口3306firewall-cmd --zonepublic --add-port3306/tcp --permanent查看防火墙状态systemctl status firewalld关闭防火墙sys…

AQS的细节--自用,非正常教程

AQS的概念 AQS叫抽象队列同步器&#xff0c;是一个框架&#xff0c;我们可以在JUC很多包看见AQS的具体实现&#xff0c;比如锁和读写锁&#xff0c;condition等&#xff0c;具有可扩展性&#xff0c;可以根据此自定义同步工具类&#xff0c;优点是系统开销低&#xff0c;实现锁…

mininet编程实现交换机规则的插入、删除与修改。_可编程网卡芯片在滴滴云网络的应用实践...

桔妹导读&#xff1a;随着云规模不断扩大以及业务层面对延迟、带宽的要求越来越高&#xff0c;采用DPDK 加速网络报文处理的方式在横向纵向扩展都出现了局限性。可编程芯片成为业界热点。本文主要讲述了可编程网卡芯片在滴滴云网络中的应用实践&#xff0c;遇到的问题、带来的收…

centos8上docker tomcat容器访问报404解决方法

目录 【README】 【1】docker安装tomcat 【2】启动多个tomcat容器 【README】 1.本文记录了 访问docker tomcat容器报404的解决方法&#xff1b; 2.附带安装tomcat步骤&#xff1b; 3.centos8 安装docker&#xff0c;refers2 centos8安装docker_PacosonSWJTU的博客-CSDN博…

ConcurrentHashMap--自用,非教学

结论先行&#xff0c;细节在下面 jdk1.7是如何解决并发问题的以及完整流程 一.首先new一个concurrentHashMap 调用默认构造方法 二.初始化 初始化initialCapacity&#xff08;默认是16&#xff0c;指一个segment内Entry的数量&#xff09;&#xff0c;loadFactor&#xff…

Java开发必须掌握的线上问题排查命令

转载自 Java开发必须掌握的线上问题排查命令作为一个合格的开发人员&#xff0c;不仅要能写得一手还代码&#xff0c;还有一项很重要的技能就是排查问题。这里提到的排查问题不仅仅是在coding的过程中debug等&#xff0c;还包括的就是线上问题的排查。由于在生产环境中&#x…

centos8启动docker-mysql8容器

【README】 本文记录了 centos8 安装&#xff0c;启动mysql8的docker容器的步骤&#xff1b; 【1】安装mysql8 docker容器 步骤1&#xff0c; 查看mysql8 docker镜像版本 &#xff1b; 最简单的方式是上 Docker Hubhttps://hub.docker.com/直接搜索mysql&#xff0c;查看其 …

Java命令学习系列(一)——Jps

转载自 Java命令学习系列&#xff08;一&#xff09;——Jpsjps位于jdk的bin目录下&#xff0c;其作用是显示当前系统的java进程情况&#xff0c;及其id号。 jps相当于Solaris进程工具ps。不象"pgrep java"或"ps -ef grep java"&#xff0c;jps并不使用应用…

springboot2.5.5配置druid数据源1.2.8与jdbc

【README】 本文记录了 springboot配置 druid数据源的步骤&#xff1b; 【1】新建springboot项目并配置druid 步骤1&#xff0c;新建springbt项目 步骤2&#xff0c;选择spring web&#xff0c;jdbc&#xff0c;mysql驱动依赖&#xff1b; 步骤3&#xff0c;添加 druid数据源…

tsc244标签编辑软件_能打小票的标签机,M110智能标签打印机来了!

每张被贴上的标签背后&#xff0c;都是对待梦想的认真、对待生活的用心&#xff0c;M110智能标签打印机为你标记美好&#xff0c;实现品质与效率兼得的追求。01、 产品简介M110智能标签打印机采取热敏无墨打印技术&#xff0c;无需碳带&#xff0c;便捷经济&#xff0c;配套“标…