rabbitmq延时队列自动解锁库存

一、库存服务自动解锁库存

在这里插入图片描述

使用了最终一致性来解决分布式事务
当order服务出现异常回滚,此时ware服务无法回滚,怎么办?

使用seata全局事务虽然能在order服务出现异常导致回滚时使其他服务的也能同时回滚,但在流量大的情况下是使用加锁的方式,效率
低不适合并发量大的情况,也可以使用定时任务轮询去查看订单的状态,但是轮询的方式比较占资源和内存,所以选用最终一致性的方案,使用mq延时队列死信路由,然后做出补救方案,只要订单服务出现故障就通过mq定时去判断,只要能保证库存最终能解锁即可

延时队列自动解锁库存业务逻辑
ware服务在完成锁库存时就给mq发消息,把消息存到死信队列中,这个消息记录了那些商品锁定多少库存,当queue到达存活时间就会把消息交给死信路由交换机,死信路由交换机会把消息发到最终的队列,如果订单支付时间为30分钟,我们就把存活时间设置为40分钟,这样就能保证我们监听的消息一定是超过了支付的时间的,然后让ware库存服务去订阅监听最终的队列即可,只要有消息我们就去检验order订单服务,只要证明订单服务出现异常回滚或者订单超过支付时间未支付的订单我们就去做一个解锁还原库存的操作

1.库存锁定成功,给mq发消息

(1)保存 工作单(订单号)、工作单详情(商品锁了多少库存)
(2)把上面的数据给mq发一份

@Transactional
    @Override
    public boolean orderStock(OrderStockRequest orderStockRequest) {
        //保存工作单
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(orderStockRequest.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        List<OrderItemVo> itemVos = orderStockRequest.getItemVos();
        List<SkuStockfromWare> collect = itemVos.stream().map(item -> {
            SkuStockfromWare skuStockfromWare = new SkuStockfromWare();
            skuStockfromWare.setSkuId(item.getSkuId());
            skuStockfromWare.setNum(item.getCount());
            //查询该商品在那些仓库有库存
            List<Long> wareId = wareSkuDao.skuStockfromWare(item.getSkuId());
            skuStockfromWare.setWareId(wareId);
            return skuStockfromWare;
        }).collect(Collectors.toList());

        //根据skuId遍历
        for (SkuStockfromWare skuStockfromWare : collect) {
            //判断是否锁定成功
            boolean flag = false;

            //判断该商品是否有仓库存在库存
            List<Long> wareIdList = skuStockfromWare.getWareId();
            if (wareIdList.size() < 0 || wareIdList == null){
                throw new NoWareStockException(skuStockfromWare.getSkuId());
            }
            for (Long wareId : wareIdList) {
                Long count = wareSkuDao.LockedStockFromWare(skuStockfromWare.getSkuId(),wareId,skuStockfromWare.getNum());
                if (count.equals(1L)){
                    //锁定成功
                    flag = true;

                    //保存工作单详情
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setSkuId(skuStockfromWare.getSkuId());
                    wareOrderTaskDetailEntity.setSkuNum(skuStockfromWare.getNum());
                    wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    wareOrderTaskDetailEntity.setWareId(wareId);
                    wareOrderTaskDetailEntity.setLockStatus(1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    //TODO 库存锁定成功->发消息给交换机
                    StockLocked stockLocked = new StockLocked();
                    stockLocked.setTaskId(wareOrderTaskEntity.getId());
                    WareOrderTaskDetailTo wareOrderTaskDetailTo = new WareOrderTaskDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity,wareOrderTaskDetailTo);
                    stockLocked.setDetailTo(wareOrderTaskDetailTo);
                    //convertAndSend(String exchange, String routingKey, Object object)
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",);

                    //该商品锁定库存成功就执行下一个商品
                    break;
                }

            }

            //如果没有一个仓库扣成功,代表此skuId的库存不足
            if (!flag){
                throw new SkuNoStockException(skuStockfromWare.getSkuId());
            }

        }
        return true;
    }

2.监听队列,解锁库存

(1)判断工作单是否存在
不存在代表锁库存操作已回滚,不做处理
(2)查询订单是否存在
如果订单不存在,表示下订单操作已回滚,执行解锁库存操作
如果存在,查询订单状态是否为 4-已关闭,如果是 4-已关闭,执行解锁库存操作,订单其他状态不做处理
(3)解锁前判断工作单的状态是否为 1-已锁定,证明只做了锁定库存操作
(4)解锁库存,修改工作单详情状态为 已解锁

/**
 * 解锁库存
 */
@RabbitListener(queues = {"stock.release.stock.queue"})
@Service
public class UnLockStockListener {

    @Autowired
    WareSkuService wareSkuService;

    @RabbitHandler
    public void UnLockStock(StockLockedTo lockedTo, Channel channel, Message message) throws IOException {
        try {
            wareSkuService.unlockStock(lockedTo);
            //签收
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            //拒签,让消息重新归队,等待服务器重启进行下一次解锁
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }

    }

}

解锁操作


    /**
     * 解锁库存
     *
     * (1).判断工作单是否存在
     *      不存在代表已回滚,不做处理
     *      (2).查询订单是否存在
     *          如果订单不存在,表示已回滚
     *             (3).执行解锁库存操作
     *          如果存在,查询订单状态是否为 4-已关闭
     *                如果是 4-已关闭,执行解锁库存操作,订单其他状态不做处理
     */
    @Override
    public void unlockStock(StockLockedTo lockedTo) {

        WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(lockedTo.getTaskId());
        //已回滚不做处理
        if (taskEntity != null){
            //查询订单是否存在
            R<OrderVo> r = orderFeignService.orderStatus(taskEntity.getOrderSn());
            if (r.getCode() == 0){
                OrderVo orderVo = r.getData(new TypeReference<OrderVo>() {
                });
                if (orderVo == null || orderVo.getStatus() == 4){
                    WareOrderTaskDetailTo detailTo = lockedTo.getDetailTo();
                    //判断工作单的状态是否为 1-已锁定,证明只做了锁定库存操作
                    if (detailTo.getLockStatus() == 1){
                        //恢复库存
                        unlock(detailTo.getId(),detailTo.getSkuNum(),detailTo.getSkuId(),detailTo.getWareId());
                    }
                }
            }else {
                throw new OrderFeignException();
            }

        }
    }

    /**
     * 解锁库存
     * UPDATE `wms_ware_sku` SET stock_locked = stock_locked - ?
     * WHERE sku_id = ? AND ware_id = ?
     */
    private void unlock(Long id,Integer skuNum, Long skuId, Long wareId) {
        wareSkuDao.unlock(skuNum,skuId,wareId);
        //修改状态为 已解锁
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(id);
        wareOrderTaskDetailEntity.setLockStatus(2);
    }

二、订单服务关闭订单同时也执行解锁库存操作

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot】从零开始封装自己的starter并且引入到其他项目中使用

从零开始封装自己的starter并且引入到其他项目中使用 简介 本文将介绍如何从零开始封装自己的starter并且引入到其他项目中使用 为什么要自己封装starter&#xff1f; 这样可以对spring以及其他第三方提供的starter做二次封装或者封装一些自己需要的内容提供给其他项目使用&…

【milvus】向量数据库,用来做以图搜图+人脸识别的特征向量

1. 安装milvus ref:https://milvus.io/docs 第一次装东西&#xff0c;要把遇到的问题和成功经验都记录下来。 1.Download the YAML file wget https://github.com/milvus-io/milvus/releases/download/v2.2.11/milvus-standalone-docker-compose.yml -O docker-compose.yml看…

行为型模式 - 策略模式

概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿&#xff0c;开发需要选择一款开发工具&#xff0c;当然可以进行代码开发的工具有很多&#xff0c;可以选择Idea进行开发&…

准备WebUI自动化测试面试?这30个问题你必须掌握(二)

本文共有11000字&#xff0c;包含了后十五个问题&#xff0c;如需要前十五个问题&#xff0c;可查看文末链接~ 16. 在WebUI自动化测试中&#xff0c;你如何处理验证码或图像识别的问题&#xff1f; 1. 人工识别&#xff1a;一种简单但费时费力的方法是使用人工手动识别验证码。…

libbpf-bootstrap 开发指南:概念与如何安装

目录 概念 如何安装& 使用 git 地址 使用git clone 下载代码 安装依赖环境 安装libbpf 编译example 概念 libbpf-bootstrap 是一个项目&#xff0c;旨在帮助开发者快速启动和开发使用 eBPF (Extended Berkeley Packet Filter) 和 libbpf 的程序。eBPF 是一种可以在…

如何用Three.js + Blender打造一个web 3D展览馆

作者&#xff1a;vivo 互联网前端团队- Wei Xing 运营活动新玩法层出不穷&#xff0c;web 3D炙手可热&#xff0c;本文将一步步带大家了解如何利用Three.js和Blender来打造一个沉浸式web 3D展览馆。 一、前言 3D展览馆是什么&#xff0c;先来预览下效果&#xff1a; 看起来像…

element-ui message消息提示组件 ①延长提示消息在页面停留时间②提示消息换行

以实现下面的效果为示例 完整代码&#xff1a; let msgList ["数据1被引用", "数据2被引用"];// 使用html的换行标签拼接信息&#xff0c;默认行距太小&#xff0c;此处用两个<br/><br/>let message 以下数据不能删除&#xff0c;原因是&…

为什么很多公司都开始使用Go语言了?

越来越多的互联网大厂开始使用Go语言了&#xff0c;譬如腾讯、美团、滴滴、百度、Google、bilibili... 还有最初使用Python的字节跳动&#xff0c;甚至已经全面拥向Go了。这么多国内外首屈一指的公司&#xff0c;都在开始使用它了&#xff0c;它到底有什么优势呢&#xff1f;这…

Redis进阶底层原理-主从复制

Redis的主从节点都会记录对方的信息&#xff0c;核心还包括ReplicationID 和 offset &#xff0c; ReplicationID &#xff1a; 主从节点实例的ID &#xff0c;redis内部就是通过这个id去识别主从节点。offset&#xff1a;数据同步偏移量&#xff0c;也就是从节点每次从主节点同…

硬中断、软中断详解

文章目录 什么是中断&#xff1f; 什么是计算机的中断&#xff1f; 什么叫硬中断、什么叫软中断&#xff1f; 怎么查看硬中断、软中断 查看硬中断的运行情况 cat /proc/interrupts 查看软中断的运行情况 cat /proc/softirqs 怎么排查软中断过高的问题&#xff1f; 软中断注意事…

noSQL的小练习

目录 Redis&#xff1a; 1、 string类型数据的命令操作&#xff1a; 2、 list类型数据的命令操作&#xff1a; 3、 hash类型数据的命令操作&#xff1a; MongoDB&#xff1a; 1. 创建一个数据库 名字grade 2. 数据库中创建一个集合名字 class 3. 集合中插入若…

接入端口与中继端口

交换机端口是支持 IT 的基本组件&#xff0c;可实现网络通信。这些有线硬件设备负责连接并允许在不同设备和连接到其端口的网络部分之间进行数据传输。由于网络管理员在确保网络连接和可用性方面发挥着关键作用&#xff0c;因此网络管理员必须清楚地了解、映射和查看其网络交换…

从小白到大神之路之学习运维第64天--------Zabbix监控mysql、ftp服务以及自定义配置

第三阶段基础 时 间&#xff1a;2023年7月19日 参加人&#xff1a;全班人员 内 容&#xff1a; Zabbix监控mysql、ftp服务以及自定义 目录 一、Zabbix监控mysql数据库 二、Zabbix监控ftp服务 三、Zabbix自定义监控项 整体zabbix搭建完成&#xff0c;server端huyang1监…

SpringBoot中整合Sharding Sphere实现数据加解密/数据脱敏/数据库密文,查询明文

场景 为防止数据泄露&#xff0c;需要在插入等操作时将某表的字段在数据库中加密存储&#xff0c;在需要查询使用时明文显示。 Sharding Sphere ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈&#xff0c; 它由Sharding-JDBC、Sharding-Proxy和Shardi…

确认应答机制与超时重发机制【TCP原理(笔记一)】

文章目录 通过序列号与确认应答提高可靠性正常的数据传输数据包丢失的情况确认应答丢失的情况发送的数据 重发超时如何确定 通过序列号与确认应答提高可靠性 在TCP中&#xff0c;当发送端的数据到达接收主机时&#xff0c;接收端主机会返回一个已收到消息的通知。这个消息叫做…

iOS 中支持点击网页scheme超链接打开其他app

网页内容如图所示 思路&#xff0c;点击网页中一个href 超链接的时候&#xff0c;会执行 decidePolicyForNavigationAction 方法&#xff0c;我们在改方法中截获URL&#xff0c; 判断如果是URL scheme类型的&#xff0c;则执行 [[UIApplication sharedApplication]openURL:URL…

Tabby - 本地化AI代码自动补全 - Windows10

参考&#xff1a; https://github.com/TabbyML/tabby 为什么选择Tabby 已经有好几款类似强劲的代码补全工具&#xff0c;如GitHub Copilot&#xff0c;Codeium等&#xff0c;为什么还要选择Tabby? Tabby除了和其他工具一样支持联网直接使用之外&#xff0c;还支持本地化部…

椒图——靶场模拟

先查看ip&#xff0c;10.12.13.232模拟的外网ip&#xff0c;其他的模拟内网ip&#xff0c;服务里面搭建好的漏洞环境。 #第一个测试项目&#xff0c;web风险发现 新建&#xff0c;下发任务&#xff0c;点威胁检测&#xff0c;webshell&#xff0c;点扫描任务&#xff0c;点新…

Star History 月度开源精选|2023 年 6 月

上一期 Star History 月度精选是写给市场、运营人员的&#xff0c;而这一期回归到 DevTools 类别&#xff0c;我们六月发现了好一些开发者可以用的不错工具&#xff01; AI Getting Started 还记得 Supabase “Build in a weekend” 的广告词吗&#xff01;AI Getting Started…

消息队列——RabbitMQ基本概念+容器化部署和简单工作模式程序

目录 基本概念 MQ 的优势 1.应用解耦 2.异步提速 3.削峰填谷 MQ 的劣势 使用mq的条件 常见MQ产品 RabbitMQ简介 RabbitMQ的六种工作模式 JMS RabbitMQ安装和配置。 RabbitMQ控制台使用。 RabbitMQ快速入门——生产者 需求: RabbitMQ快速入门——消费者 小结 基本概…