聊聊 分布式系统 中的补偿机制设计问题

news2024/9/19 10:53:29
  • 一、关于业务补偿机制

    • 1、什么是业务补偿

    • 2、业务补偿设计的实现方式

  • 二、关于回滚

    • 1、显示回滚

    • 2、回滚的实现方式

  • 三、关于重试

    • 1、重试的使用场景

    • 2、重试策略

    • 3、重试时的注意事项

  • 四、业务补偿机制的注意事项

    • 1、ACID 还是 BASE

    • 2、业务补偿设计的注意事项


我们知道,应用系统在分布式的情况下,在通信时会有着一个显著的问题,即一个业务流程往往需要组合一组服务,且单单一次通信可能会经过 DNS 服务,网卡、交换机、路由器、负载均衡等设备,而这些服务于设备都不一定是一直稳定的,在数据传输的整个过程中,只要任意一个环节出错,都会导致问题的产生。

这样的事情在微服务下就更为明显了,因为业务需要在一致性上的保证。也就是说,如果一个步骤失败了,要么不断重试保证所有的步骤都成功,要么回滚到以前的服务调用。

因此我们可以对业务补偿的过程进行一个定义,即当某个操作发生了异常时,如何通过内部机制将这个异常产生的「不一致」状态消除掉。

一、关于业务补偿机制

1、什么是业务补偿

我们知道,应用系统在分布式的情况下,在通信时会有着一个显著的问题,即一个业务流程往往需要组合一组服务,且单单一次通信可能会经过 DNS 服务,网卡、交换机、路由器、负载均衡等设备,而这些服务于设备都不一定是一直稳定的,在数据传输的整个过程中,只要任意一个环节出错,都会导致问题的产生。

这样的事情在微服务下就更为明显了,因为业务需要在一致性上的保证。也就是说,如果一个步骤失败了,要么不断重试保证所有的步骤都成功,要么回滚到以前的服务调用。

因此我们可以对业务补偿的过程进行一个定义,即当某个操作发生了异常时,如何通过内部机制将这个异常产生的「不一致」状态消除掉。

2、业务补偿设计的实现方式

业务补偿设计的实现方式主要可分为两种:

  • 回滚(事务补偿) ,逆向操作,回滚业务流程,意味着放弃,当前操作必然会失败;

  • 重试 ,正向操作,努力地把一个业务流程执行完成,代表着还有成功的机会。

一般来说,业务的事务补偿都是需要一个工作流引擎的。这个工作流引擎把各式各样的服务给串联在一起,并在工作流上做相应的业务补偿,整个过程设计成为最终一致性的。

Ps:因为「补偿」已经是一个额外流程了,既然能够走这个额外流程,说明时效性并不是第一考虑的因素。所以做补偿的核心要点是:宁可慢,不可错。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

二、关于回滚

“回滚” 是指当程序或数据出错时,将程序或数据恢复到最近的一个正确版本的行为。在分布式业务补偿设计到的回滚则是通过事务补偿的方式,回到服务调用以前的状态。

1、显示回滚

回滚一般可分为 2 种模式:

  • 显式回滚 ;调用逆向接口,进行上一次操作的反操作,或者取消上一次还没有完成的操作(须锁定资源);

  • 隐式回滚 :隐式回滚意味着这个回滚动作你不需要进行额外处理,往往是由下游提供了失败处理机制的。

最常见的就是「显式回滚」。这个方案无非就是做 2 个事情:

  • 首先要确定失败的步骤和状态,从而确定需要回滚的范围。一个业务的流程,往往在设计之初就制定好了,所以确定回滚的范围比较容易。但这里唯一需要注意的一点就是:如果在一个业务处理中涉及到的服务并不是都提供了「回滚接口」,那么在编排服务时应该把提供「回滚接口」的服务放在前面,这样当后面的工作服务错误时还有机会「回滚」。

  • 其次要能提供「回滚」操作使用到的业务数据。「回滚」时提供的数据越多,越有益于程序的健壮性。因为程序可以在收到「回滚」操作的时候可以做业务的检查,比如检查账户是否相等,金额是否一致等等。

2、回滚的实现方式

对于跨库的事务,比较常见的解决方案有:两阶段提交、三阶段提交(ACID)但是这 2 种方式,在高可用的架构中一般都不可取,因为跨库锁表会消耗很大的性能。

高可用的架构中一般不会要求强一致性,只要达到最终的一致性就可以了。可以考虑:事务表、消息队列、补偿机制、TCC 模式(占位 / 确认或取消)、Sagas模式(拆分事务 + 补偿机制)来实现最终的一致性。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

三、关于重试

“重试” 的语义是我们认为这个故障是暂时的,而不是永久的,所以,我们会去重试。这个操作最大的好处就是不需要提供额外的逆向接口。这对于代码的维护和长期开发的成本有优势,而且业务是变化的。逆向接口也需要变化。所以更多时候可以考虑重试。

1、重试的使用场景

相较于回滚,重试使用的场景要少一些:下游系统返回请求超时,被限流中等临时状态的时候,我们就可以考虑重试了。而如果是返回余额不足,无权限的明确业务错误,就不需要重试。一些中间件或者 RPC 框架,返回 503,404 这种没有预期恢复时间的错误,也不需要重试了。

2、重试策略

重试的时间和重试的次数。这种在不同的情况下要有不同的考量,主流的重试策略主要是以下几种:

策略 1 - 立即重试 :有时候故障是暂时性的,可能因为网络数据包冲突或者硬件组件高峰流量等事件造成的,在这种情况下,适合立即重试的操作。不过立即重试的操作不应该超过一次,如果立即重试失败,应该改用其他策略;

策略 2 - 固定间隔 :这个很好理解,比如每隔 5 分钟重试一次。PS:策略 1 和策略 2 多用于前端系统的交互操作中;

策略 3 - 增量间隔 :每一次的重试间隔时间增量递增。比如,第一次 0 秒、第二次 5 秒、第三次 10 秒这样,使得失败次数越多的重试请求优先级排到越后面,给新进入的重试请求让路;

return (retryCount - 1) * incrementInterval;

策略 4 - 指数间隔: 每一次的重试间隔呈指数级增加。和增量间隔一样,都是想让失败次数越多的重试请求优先级排到越后面,只不过这个方案的增长幅度更大一些;

return 2 ^ retryCount;

策略 5 - 全抖动: 在递增的基础上,增加随机性(可以把其中的指数增长部分替换成增量增长。)适用于将某一时刻集中产生的大量重试请求进行压力分散的场景;

return random(0 , 2 ^ retryCount);

策略 6 - 等抖动: 在「指数间隔」和「全抖动」之间寻求一个中庸的方案,降低随机性的作用。适用场景和「全抖动」一样。

int baseNum = 2 ^ retryCount;
return baseNum + random(0 , baseNum);

策略 - 3、4、5、6 的表现情况大致是这样(x轴为重试次数):

3、重试时的注意事项

首先对于需要重试的接口,是需要做成幂等性的,即不能因为服务的多次调用而导致业务数据的累计增加或减少。

满足「幂等性」其实就是需要想办法识别重复的请求,并且将其过滤掉。思路就是:

  • 给每个请求定义一个唯一标识。

  • 在进行「重试」的时候判断这个请求是否已经被执行或者正在被执行,如果是则抛弃该请求。

Ps:此外重试特别适合在高负载情况下被降级,当然也应当受到限流和熔断机制的影响。当重试的“矛”与限流和熔断的“盾”搭配使用,效果才是最好。

四、业务补偿机制的注意事项

1、ACID 还是 BASE

ACID 和 BASE 是分布式系统中两种不同级别的一致性理论,在分布式系统中,ACID有更强的一致性,但可伸缩性非常差,仅在必要时使用;BASE的一致性较弱,但有很好的可伸缩性,还可以异步批量处理;大多数分布式事务适合 BASE。

而在重试或回滚的场景下,我们一般不会要求强一致性,只要保证最终一致性就可以了!

2、业务补偿设计的注意事项

业务补偿设计的注意事项:

  • 因为要把一个业务流程执行完成,需要这个流程中所涉及的服务方支持幂等性。并且在上游有重试机制;

  • 我们需要小心维护和监控整个过程的状态,所以,千万不要把这些状态放到不同的组件中,最好是一个业务流程的控制方来做这个事,也就是一个工作流引擎。所以,这个工作流引擎是需要高可用和稳定的;

  • 补偿的业务逻辑和流程不一定非得是严格反向操作。有时候可以并行,有时候,可能会更简单。总之,设计业务正向流程的时候,也需要设计业务的反向补偿流程;

  • 我们要清楚地知道,业务补偿的业务逻辑是强业务相关的,很难做成通用的;

  • 下层的业务方最好提供短期的资源预留机制。就像电商中的把货品的库存预先占住等待用户在 15 分钟内支付。如果没有收到用户的支付,则释放库存。然后回滚到之前的下单操作,等待用户重新下单。

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

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

相关文章

Langchain+本地大语言模型进行数据库操作的实战代码

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

Navicat Premium 16执行.sql语句中含有汉字乱码造成view和function创建后无法使用

Navicat Premium 16执行.sql语句中含有汉字乱码造成view和function创建后无法使用 如图,从这里选择sql时没法改sql。所以造成我昨天创建view和function时创建好的前面有感叹号没法用。打开一个fun看里面的汉字是问号。 所以要从这里打开: 1. ultraedit…

EMC学习笔记(十)特殊信号的EMC处理(二)

特殊信号的EMC处理(二) 1.对外接口的EMC设计标准电路1.1 DVI EMC设计标准电路1.2 HDMI接口EMC设计标准电路1.3 LVDS接口EMC设计标准电路1.4 PS2接口EMC设计标准电路1.5 RJ11 EMC设计标准电路1.6 SCART接口EMC设计标准电路1.7 s-video接口EMC设计标准电路…

五个步骤,助你优雅的写好 Controller 层代码!

Controller 层逻辑 普通写法 优化思路 Controller 层逻辑 MVC架构下,我们的web工程结构会分为三层,自下而上是dao层,service层和controller层。controller层为控制层,主要处理外部请求,调用service层。 一般情况下…

6.23黄金是否会跌破1900?多单被套怎么办?

近期有哪些消息面影响黄金走势?今日黄金多空该如何研判? ​黄金消息面解析:周四(6月22日)美市尾盘,现货黄金收报1910美元/盎司,下跌20美元或0.1%,日内最高触及1934.95美元/盎司&…

C++ 面向对象(1)——类 对象

C 在 C 语言的基础上增加了面向对象编程,C 支持面向对象程序设计。类是 C 的核心特性,通常被称为用户定义的类型。 类用于指定对象的形式,是一种用户自定义的数据类型,它是一种封装了数据和函数的组合。类中的数据称为成员变量&a…

Studio One6中文版多少钱?有哪些新功能

Studio One6中文版现在有三个版本,免费版,Artist,Pro版本。下载后是免费版,免费版没有时间限制,但是功能受限。三个版本都支持win/mac系统,而且同时支持5台设备使用,还可以换机使用。 三个版本…

Spring Cloud Day2 Nacos配置管理、Feign远程调用与Gateway服务网关

SpringCloud实用篇02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我…

关闭 MAC 的 Microsoft AutoUpdate 自动更新

不是我说,这玩意儿看着是真不爽!!而且每天都要弹出来搞事情!!! 我宣布:今天就要永久关闭 MAC 的 Microsoft AutoUpdate 自动更新!! 像我一样的朋友请举手!&am…

Linux学习[17]bash学习深入3---万用字符特殊符号---数据流重导向

文章目录 前言1. 万用字符2. 特殊字符3. 数据流重导向3.1标准输出3.2 标准输入 总结 前言 这篇博客是对之前在查找的时候涉及到的一些通配符(bash里面就是万用字符)的整理。这个为后面管线相关打一个基础。 1. 万用字符 这里整理了一个表格,后面配上相关实例。 符…

定制化你的应用外观:gradio的自定义主题功能

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

Axure教程——多项选择器

本文介绍利用Axure里的中继器和动态面板制作一个多选下拉列表 一、效果 预览地址:https://frh0rc.axshare.com 二、功能 1、点击下拉框可以弹出选项,点击选项可以选中选项2、用户可以取消选中 三、制作 1、制作下拉框 拖入一个矩形组件,命名为“下拉框…

C++——指针空值

在良好的C/C编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化: void TestPtr() {int*…

python---案例分析(2)

例5: 使用python生成一个二维码 结果就会显示一个二维码!拿出手机扫描二维码就可以看到make中填写的内容! 例6: 操作excel 使用python计算平均分的情况 首先在自己的pycharm上安装xlrd 必须是上述版本的 安装成功版本后,import一下即可使用 以下是计算100班的平均分 例6: …

后端开发通用

1、前后端开发 项目基于前后端分离的架构进行开发,前后端分离架构总体上包括前端和服务端,通常是多人协作开发 对于后端java工程师 把精力放在设计模式,springspringmvc,linux,mysql事务隔离与锁机制,mongo…

typescript找不到模块‘vue‘ ‘vue-router‘

import { createRouter, createWebHashHistory, createWebHistory } from vue-router 提示:找不到模块“vue-router”。你的意思是要将 "moduleResolution" 选项设置为 "node",还是要将别名添加到 "paths" 选项中?ts(27…

Python基础篇(五):函数的定义和调用

Python基础篇(四):基本数据类型的学习和示例 函数的定义和调用 前言1. Python 函数示例2. 自定义函数2.1 函数语法2.2 函数示例2.3 函数调用 3.内置函数3.1 数学函数3.2 类型转换函数3.3 序列操作函数3.4 输入输出函数3.5 文件操作函数3.6 迭代函数3.7 集合操作函数…

C#期末考试总结:

考点1:内插字符串$(使用方法:$"........{变量名}.....",作用:可读性增强,本身个也是一个字符串,可以作为一个变量赋值,有利于字符串的生成 考点2:强类型语言 考点3&…

【Unity之IMGUI】—自定义常用控件的封装(即拿即用)

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:Uni…

为什么有了HTTP,还需要WebSocket协议?

目录 WebSocket是什么? WebSocket怎样建立连接? WebSocket的实际用途 WebSocket 与 HTTP 的选择 HTTP是基于TCP协议的,同一时间里,客户端和服务器只能有一方主动发数据,是半双工通信。 通常,打开某个网…