Go微服务: 分布式之通过可靠消息实现最终一致性

通过可靠消息实现最终一致性

  • 可靠消息,就是靠普消息,还是基于之前的这个案例
  • 比如这个订单服务,无论你是先发送消息,还是先新建订单,它其实都是发送的不可靠消息
  • 就是说如果这个消息,像mysql事务那样,只要订单服务不确认,下游就没办法消费
  • 如果你这个订单服务挂了,就可以取消这个消息,就不用做这个本地消息表了
  • 本地消息表,要有一个这个循环的这么一个查询,高并发的时候,你本地的数据库本身压力就大
  • 再弄这么一个查询,一直循环的查,它这个压力也不小,现在看一下基于事务消息,也称为可靠消息
  • 生产者就可以理解为这个订单服务,消费者就可以理解为积分服务,还有库存服务
  • 先看第一个,生产者先发一个半消息给消息队列,消息队列就回我这个半消息成功的信息
  • 这是一个半消息,然后拿到这个半消息成功的结果之后,我们往数据库里写一个Transaction事务
  • 我们就把本地事务执行了,执行之后,也就是到了第4步, 成功情况下就是确认这个半消息
  • 只要到第4步这个commit成功了,消费者就可以消费了,因为你的这个消息已经在这个消息队列中了
  • 这个解决方式是解决了只要我们能发出去的这个消息就是可靠的,只要能提交的消息,本地消息就一定是成功的
  • 因为你本地事务都已经执行成功了,先发半消息,消息队列回给我,回给我之后,我就能收到了,说明已经成功了
  • 然后这个时候你就开始干你本地的事儿,比如订单生产者自己开始建订单,建订单产品表,这都是你的事
  • 我本地能成功了之后,1,2,3步是为了能让消费者成功消费的准备工作
  • 这个思路就是说只要一提交,那我本地这边全部ok, 然后我这个消息队列, 肯定能保证我我的最终一致性
  • 如果在3之前出错,那不会做事务,那相当于准备工作没做好,那下游也不会做相应的这个事情
  • 同时,我们还要思考,有没有其他方面的情况会导致问题
    • 比如,更复杂的网络传输问题,别人的服务宕机了或有bug了
    • 因为微服务在开发的时候,每个小组可以时刻发布自己的服务,它不受控制
  • 如果上述分布式服务出问题了,消息队列也会有一个回查事务消息状态的机制
  • 我会问你这个生产者,哎,你这个状态是啥?然后生产者就会查询这个本地事务状态
  • 第5步和第6步,就是又一次为这个返回事务状态做commit和rollback,就是为下一步的工作做准备
  • 就是消息队列,不知道你这个消息要不要投递,也不能知道别人的状态是什么样的
  • 那消息队列就会问这个生产者,你这个消息事务是啥状态,在第6步得到查询的事务状态是commit
  • 那我消息队列就commit消息投递,一旦消息投递了,这个消费者就可以进行消费了
  • 假如说,返回的这个事务状态是rollback,消息队列就可以把消息扔了
  • 在这整个链路里,有成功的,让消费者消费消息;也有失败,让这个消息队列去丢弃消息
  • 还有中间状态,就是说我不确定这个事务是不是正确的?那询问还是有结果的,就是 commit 或 rollback
  • 其实我们说走到第5步,第6步,还有第7步的时候,他就可以再一次确认我们的消息是否要投递还是丢弃
  • 到这里,一直没有看到说有锁的存在,在高并发的情况下,消息队列就保证了我们最终的一致性
  • 就是说锁的存在,它一定是和高并发是这个对立的,我们尽量不要用锁的方式去考虑我们的并发

积分和库存业务场景的对比

  • 再回过头来看一下这个模型,如果你的生产者是订单,而消费者是库存的话
  • 如果库存不足,消费者是库存服务,库存不足,虽然成功的发送了到这个RocketMQ里
  • 但是库存没有办法成功消费这条消息,这个和其他业务形态上是不一样的
  • 比如订单服务,可以说是送积分,从技术角度来说,积分是没有上限的
  • 还有一种形态,就是我们说发短信,你成功的购买了某某产品等等
  • 这种业务形态, 你的生产者只要把消息放到了消息队列, 消息队列一定是可以保障的
  • 就是说你消息队列是集群吧,你在不挂的情况下,是一定能送达到消费者应用队列里的
  • 比如说, 积分服务,短信服务,你这边已经是commit了
  • 无论你是在第4步commit的,还是说第7步commit的下游是一定能消费到的
  • 但是库存服务不一样,如果库存不足了,你这边又没办法返回
  • 左边是库存服务和订单服务,右边是消息队列
  • 从正向来说,如果服务的提供方发送消息到这个消息队列,也就是生产者发送消息到消息队列
  • 只要消息队列集群不挂,那么我们的消费者是一定能收到这个消息的
  • 就是实现最终的一致性对于积分服务,短信服务的使用都是没有问题的
  • 因为积分理论上是无上限的,我们的短信是一定能发上去的,只要你短信账户里,有足够的余额
  • 但是当库存为零的时候,你的这个消息队列仍然收到了我们发送成功的消息
  • 但下游库存服务是没有办法消费成功的,我们不可能凭空多出来这么多库存
  • 让你去消费,因为没有那么多库存,我们又不能把这个消息队列成功投递库存不足的消息
  • 再返回给这个生产者,这个是不可能的,所以,我们能不能先发送一个归还库存的半消息
  • 这个半消息, 对于库存服务来说, 它是见不到的,我们发完半消息之后
  • 去调用扣减库存的Srv服务,这就是一个Grpc的这么一个调用,先看这个返回失败的情况
  • 如果调用库存失败了,这个时候返回一个rollback,因为一开始就是调用的是归还
  • 那你这个时候就调用rollback,如果我们库存执行失败了,那说明我们库存这个数据是没有变的
  • 既然你库存调用是失败,订单就不会创建, 因为我本地这个数据库就不会变
  • 那么我们两边的这个数据都没变,这个业务也是可以接受的
  • 就是说没有造成数据不一致,那你就不要给我发这个消息来告诉我,你要去归还库存了
  • 那我们直接rollback这个消息,然后这个消息队列, 就可以把这个归还库存的消息给它扔掉了
  • 扔掉之后,看左边这一边就没问题了,数据都没变
  • 你执行失败了,我这边又没执行,或者说,这两边的数据都保持一致,这就是OK的
  • 好,我们再看下一张执行成功的图,看我们订单服务发送一个半消息
  • 然后调用了Grpc扣减那个库存,然后它成功了
  • 成功之后, 我们就会执行这个本地 mysql 事务服务, 其实它可能成功,也可能失败
  • 因为我们如果在微服务里, 由于网络原因或宕机,Bug,停电等各种问题
  • 它都可能导致一个服务的运行失败
  • 如果我们先说这个执行本地mysql事务成功,在第4步成功,执行rollback
  • 还是从数据的角度来看,库存扣减成功,订单执行成功,我们本地也执行成功
  • 那就是说我们两边数据都改变了,这个业务上也是可以接受的
  • 订单生成成功了,库存也扣减了,那就相当于交易成功
  • 那你就不要给我发送这个归还库存的半消息了,所以是 rollback这个半消息
  • 然后,我们把这个消息就扔掉了
  • 那我们再看看,如果第4步执行失败了,就是说我们有任何情况
  • 订单服务有bug,断电或者其他场景,执行失败,那就执行一个commit
  • 因为订单执行失败了,相当于订单数据没有变,那你的库存现在是变了
  • 因为之前第三步已经执行成功了,那这个时候就要告诉库存,说给我扣减了,因为我执行失败了
  • 失败以后,提交一个commit之后,然后,这个库存服务就可以监听到这个消息队列里
  • 因为它 commit 了嘛,这个消息就能看到了,订阅了这个消息之后,就能去归还库存了
  • 如果你这个订单服务,还有一些其他问题,怎么办?其实这个消息队列还提供一种机制
  • 就是回查这个消息,比如说我现在不确定你这个是要提交还是rollback
  • 这个回查机制,就查这个订单的服务,那你还去你这个本地事务的数据库库里去捞数据
  • 你捞对了,就rollback,捞错了,还是commit, 你commit之后
  • 我还是能调用到这个库存服务,然后你再给我归还
  • 这张图就是看到了为了保持这个数据的一致性,我们这边整个业务流程的保证就是这样了
  • 再看上面这张图,还有问题,是在原来的基础上增加了第8条发送延迟消息和监听延迟消息。
  • 就是说我这个库存有一百个,我这个用户买完以后,就是不支付
  • 如果他一周不支付或者一个月不支付,你的库存永远不释放,别人永远买不了
  • 那不就把这个商城的库存给锁死了
  • 当我库存执行成功,这个本地的事务也执行成功的时候,我就把它发送一条延迟消息。
  • 假如我们规定半个小时,时间一到,这个延迟消息就会投递
  • 然后,我们就会根据这个消息去看,这个订单是 支付成功了,还是支付失败了
  • 如果是未支付或者是支付失败都可以,如果你执行失败了,那我就归还库存
  • 因为对商城来说,如果没收到钱,那我就归还库存,半个小时之后,仍然其他的用户就可以买
  • 这样就完美的解决了库存,订单和这个订单下单成功后不支付的这么一个场景
  • 这里的核心重点是:在一开始发送了一个归还库存的半消息
  • 执行commit和rollback的情况是反着来的

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

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

相关文章

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第七周) - 结构化预测

结构化预测 0. 写在大模型前面的话1. 词法分析 1.1. 分词1.2. 词性标注 2.2. 句法分析 2.3. 成分句法分析2.3. 依存句法分析 3. 序列标注 3.1. 使用分类器进行标注 4. 语义分析 0. 写在大模型前面的话 在介绍大语言模型之前,先把自然语言处理中遗漏的结构化预测补…

【机器学习】机器学习与医疗健康在智能诊疗中的融合应用与性能优化新探索

文章目录 引言机器学习与医疗健康的基本概念机器学习概述监督学习无监督学习强化学习 医疗健康概述疾病预测诊断辅助个性化治疗方案制定 机器学习与医疗健康的融合应用实时健康监测数据预处理特征工程 疾病预测与优化模型训练模型评估 诊断辅助与优化深度学习应用 个性化治疗方…

接口自动化Requests+Pytest基础实现

目录 1. 数据库以及数据库操作1.1 概念1.2 分类1.3 作用 2 python操作数据库的相关实现2.1 背景2.2 相关实现 3. pymysql基础3.1 整个流程3.2 案例3.3 Pymysql工具类封装 4 事务4.1 案例4.2 事务概念4.3 事务特征 5. requests库5.1 概念5.2 角色定位5.3 安装5.4 校验5.5 reques…

“中新美”三重身份,能帮SHEIN解决上市问题吗?

一家公司的海外上市之路能有多复杂?辗转多地的SHEIN,可能是当前最有话语权回答这个问题的公司。最近,它又有了新消息。 在上市信息多次更改后,伦敦正在成为SHEIN最有可能的“着陆”点。巴伦周刊援引英国天空新闻报道称&#xff0…

Python01 -分解整包数据到各个变量操作和生成器

Python 的星号表达式可以用来解决这个问题。比如,你在学习一门课程,在学期末的时候,你想统计下家庭作业的平均成绩,但是排除掉第一个和最后一个分数。如果只有四个分数,你可能就直接去简单的手动赋值,但如果…

5、搭建前端项目

5.1 使用vite vue搭建 win r 打开终端 切换到你想要搭建的盘 npm init vitelatest跟着以下步骤取名即可 cd fullStackBlognpm installnpm run dev默认在 http://localhost:5173/ 下启动了 5.2 用vscode打开项目并安装需要的插件 1、删除多余的 HelloWorld.vue 文件 2、安装…

【Vue3】理解toRef() 和 toRefs()

历史小剧场 知道可能面对的困难和痛苦,在死亡的恐惧中不断挣扎,却仍然能战胜自己,选择这条道路,这才是真正的勇气。----《明朝那些事儿》 前言 toRef 和 toRefs 是Vue3中的响应式转换工具函数 toRef: 不影响源对象的情况下&#x…

【数据结构】AVLTree实现详解

目录 一.什么是AVLTree 二.AVLTree的实现 1.树结点的定义 2.类的定义 3.插入结点 ①按二叉搜索树规则插入结点 ②更新平衡因子 更新平衡因子情况分析 ③判断是否要旋转 左单旋 右单旋 左右单旋 右左双旋 4.删除、查找和修改函数 查找结点 三.测试 1.判断是否是搜索树 …

面试题-Vue2和Vue3的区别

文章目录 1. 响应式系统2. 组合式 API (Composition API)3. Fragment (碎片)4. Teleport (传送门) 5. 性能改进6. 移除或改变的功能7. 构建工具8. TypeScript 支持 Vue 2 和 Vue 3 之间存在许多重要的区别,这些区别涵盖了性能、API 设计、组合式 API(Com…

AndroidStudio无法识别连接夜神模拟器

方法一(无法从根本上解决) ①进入夜神模拟器安装路径下的bin路径(安装路径可以带有中文路径) ②打开cmd窗口,输入以下代码(一定要打开模拟器) nox_adb.exe connect 127.0.0.1:62001 方法二(根本上解决) 原因:Android Studio的adb版本与夜神模拟器的adb版本不一致 ①打开And…

技术架构的发展

技术架构的演进 主要方向: ​ 1.提高单位时间内的吞吐量,提高并发度; ​ 2.对应用服务代码进行解耦合,使得开发效率得到提高; ​ 3.运维成本降低; ​ 4.成本降低,如购买云厂商资源&#xf…

Cortex-M7——NVIC

Cortex-M7——NVIC 小狼http://blog.csdn.net/xiaolangyangyang 一、NVIC架构 二、中断及异常编号 三、中断屏蔽寄存器(__disable_irq和__enable_irq操作的是PRIMASK寄存器) 四、中断分组寄存器(SCB->AIRCR[10:8]) 五、NVIC寄…

常用的Linux命令,linux下文件的读、写、打开、关闭append用法

vim demoq.c打开写的.c文件 内容为 按a可以编辑页面代码。按ESC退出编辑然后按shift:wq保存文件并退出 Linux 系统中采用三位十进制数表示权限,如0755, 0644.7 124(可读、可写、可执行) 5 14(可读、不可写、可执行) …

苹果手机微信如何直接打印文件

在快节奏的工作和生活中,打印文件的需求无处不在。但你是否曾经遇到过这样的困扰:打印店价格高昂,让你望而却步?今天,我要给大家介绍一款神奇的微信小程序——琢贝云打印,让你的苹果手机微信直接变身移动打…

Docker配置Redis集群以及主从扩容与缩容

基础镜像拉取 docker run -p 6379:6379 -d redis:6.0.8 配置文件以及数据卷挂载 # 开启密码验证(可选) requirepass 1234 # 允许redis外地连接,需要注释掉绑定的IP # bind 127.0.0.1 # 关闭保护模式(可选) protected-m…

6.18云服务器大促盘点,错过一次,再等一年!

随着云计算技术的飞速发展,云服务器已成为企业和个人构建和扩展在线业务的首选平台。特别是在大型促销活动如618年中大促期间,云服务提供商纷纷推出极具吸引力的优惠,以降低用户上云的门槛。以下是对当前市场上几个主流云服务提供商的优惠活动…

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第六周) - 预训练模型

预训练模型 1. 预训练模型介绍 1.1. ELMo1.2. GPT1.3. BERT 2. Seq2Seq 2.1. T52.2. BART 3. Tokenization 1. 预训练模型介绍 在预训练语言模型出现之前,统计语言模型(如N-gram模型)是主流方法。这些模型利用统计方法来预测文本中的下一个…

设计模式-外观(门面)模式(结构型)

外观模式 外观模式又称门面模式(结构型模式),它是一个可以屏蔽系统复杂性的设计模式。俗话说没有什么问题是加一层“介质”解决不了的,如果有那就在加一层。在开发过程中肯定封装过Utils类,我认为这就是一种门面模式&…

亘古真知

目录 一,概述 二,个人面板 三,科技面板 四,手牌 五,回合 1,行动 (1)打造 (2)学习 (3)归档 (4)挖掘 …

Java——数组排序和查找

一、排序介绍 1、排序的概念 排序是将多个数据按照指定的顺序进行排列的过程。 2、排序的种类 排序可以分为两大类:内部排序和外部排序。 3、内部排序和外部排序 1)内部排序 内部排序是指数据在内存中进行排序,适用于数据量较小的情况…