请选择 进入手机版 | 继续访问电脑版

聊聊微服务架构中的事务处理

[复制链接]
小小海 发表于 2021-1-2 19:00:00 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
当从一个单体系统转向微服务架构(microservice architecture, MSA)时,处理处罚分布式系统带来的复杂性是一个挑战。事务处理处罚是此中的首要焦点问题。在一个 Web 应用步伐中使用当地事务完成的范例数据库事务,现在是一个复杂的分布式事务问题。在本文中,我们将讨论造成这种情况的原因、大概的办理方案以及使用 MSA 开发安全事务性软件系统的最佳实践。
数据库事务:入门

在我们旧的单体应用中,我们用数据库事务来实现全部或全无的数据利用,同时包管数据一致性。我们主要使用 ACID 事务,这些可以在关系型数据库系统中见到。下面是快速回首:


  • 原子性(Atomicity): 所有的利用都乐成执行大概所有都一起失败。
  • 一致性(Consistency): 数据库中的数据保持有效状态,遵循其规则,比方引用完整性。
  • 隔离性(Isolation): 分离的事务并行运行,不会相互干扰。这应该是因为事务在其隔离的情况中运行,别的事务不能看到期间发生的变革。
  • 长期性(Durability): 事务提交之后,更改会被长期地生存,比方长期化在硬盘上,因此数据库服务器的暂时瓦解不会丢失数据。
我们可以想象使用如下所示的 ACID 事务,将资金从一个人的账户转移到另一个人的账户。
SQL
  1. BEGIN TRANSACTION  UPDATE ACCOUNT SET BALANCE=BALANCE-AMOUNT WHERE ID=1;  UPDATE ACCOUNT SET BALANCE=BALANCE+AMOUNT WHERE ID=2; END TRANSACTION
复制代码
这里,我们将个体的借贷利用包装在一个 ACID 事务中。这制止了不一致的情况,比方,如果资金从一个人的账户中取出,但没有存入另一个人的账户中,就会从系统中丢失。这是一个清晰、直接的办理方案,我们将在需要时继承编写这样的代码。
我们习惯于在任何需要的时候使用 ACID 事务。对于处理处罚需求被生存在单个数据库服务器的大部门范例用户而言,这个模子很好。但是对于需要随着数据访问、存储性能和读写规模等需求的增长而扩充系统的用户而言,这种架构很快就会瓦解。对于这些用户,有两种方法来扩展数据存储:


  • 垂直扩展:向单个服务器增加更强力的硬件,比方增强的 CPU 和 RAM 内存,大概移到一个大型盘算机上。这通常很贵,而且当硬件再也不能升级时就会到达一个瓶颈。
  • 水平扩展:添加更多服务器节点,所有东西都通过盘算机网络毗连。这通常是最实际的实施方案。
在水平扩展中,一个数据存储拥有一个集群中的多个节点,事情就会变得有点儿复杂。由于数据驻留在物理隔离的服务器上,因此出现了一系列新的挑战。CAP 定理表明白这一点,在分布式数据存储中以下属性只可以实现此中两点。


  • 一致性:这涉及节点之间的数据一致性。数据会有一些副本,来在发生故障时提供冗余性并提高读取性能。这意味着,当一个数据在单个地方完成更新时,它应该在所有别的副本同步被更新,而不会给客户端带来任何延迟。这通常被称为具有线性化本领。如你所见,这与 ACID 中的一致性概念并不相同。
  • 可用性:分布式数据存储是高度可用的,因此某个服务器实例的丢失不会妨碍整个数据存储的功能,用户仍将得到没有错误的请求响应。
  • 分区容差:数据存储可以处理处罚网络分区。网络分区是指网络中某些节点之间的通信中断。因此,某些节点大概无法与数据存储集群中的别的节点通信。在数据库集群的节点来看,这会将数据存储节点分别为多个当地网络。外部客户端仍然可以大概访问数据库集群中的所有节点。
根据这一点,不大概同时拥有一致性、可用性和分区容差。如果我们思量大概发生的情况,我们就能直观地明确这种行为。
为了在写入数据时保持一致性,我们需要向所有副本服务器同时写入数据。然而,如果网络中有分区,我们就不能这么做,因为我们当时候只能访问某些服务器。在那种情况下,如果我们想要在保持一致性的同时容忍这些分区,我们就不能让用户从数据存储中读取这些不一致的数据。那意味着我们需要停止响应用户请求,使数据存储不再可用(一致性和分区容差)。
另一个场景会使得数据存储继承工作,即保持数据存储的可用性,但会使得数据存储不再保持一致性。(可用性和分区容差)。
最后一种场景是系统不能容忍分区,使得可以保持一致性和可用性。在这种情况下,为了拥有很强的一致性(线性化),我们仍然需要使用诸如两段提交(2PC)等事务协议来执行差异副本数据库服务器节点之间的数据利用。在 2PC 中,加入的数据库的利用是由一个事务协调器以如下两段执行的:



  • 准备:加入者被要求查抄它的数据利用是否可以大概执行,并向事务协调器提供一个允许,如果需要,它可以提交利用或回滚。这对于所有加入者都是这样要求的。
  • 提交:如果所有加入者在准备阶段都说“可以”,那么每个加入者都会得到一个提交利用来提交前面提到的利用。此时,加入者不能拒绝该利用,因为之前已经向事务协调器做出了允许。如果任何先前的加入者因为某种原因拒绝了它们的当地数据利用,协调器会向所有的加入者发送回滚下令。
除了上述用于伸缩性的数据库副本场景,2PC 还用于在差异范例的系统(如数据库服务器和消息署理)之间执行事务。然而,我们通常制止使用 2PC,因为在分布式加入者中增加锁争用,会导致性能低下并妨碍伸缩性。
实际上,盘算机网络是不可靠的,我们应该预期它们会在某个时间存在网络分区。因此,我们通常看到数据库系统优先思量可用性或一致性,即 AP 或 CP。一些系统允许用户调解这些参数,来使其得到高可用性大概选择一致性级别。这在 Amazon 的 DynamoDB 和 Apache Cassandra 的数据库系统中已有提供。然而,它们通常被归类为 AP 系统,因为它们没有严格的 CAP 级别的一致性。Cassandra 的轻量事务支持使用 Paxos 共识协议来实现线性化的一致性,但是这很少使用,因为它的机制会导致非常低的性能,这是可以预见的。
可伸缩性对数据一致性的影响

当我们研究 CAP 定理的权衡时,如果我们重视高可用性和高性能与可伸缩性,那我们就必须在数据一致性上做出妥协。CAP 的数据一致性影响 ACID 的隔离性。如果分布式系统中的节点之间的数据不一致,这意味着存在事务隔离问题,而且我们会看到脏数据。因此,在这种情况下,我们必须继承现实,找到一种没有 ACID 事务和完全的 CAP 一致性的工作方式。
这种包罗最终一致性的事务一致性模子也被称为 BASE(BasicallyAvailable,Soft State,andEventually Consistent),它促进了拥有最终一致性的可用性。软状态意味着,数据大概为了最终一致性而在之后变革。大多数 NoSQL 数据库遵循这种方案,它们不提供任何 ACID 事务功能,而是聚焦于可伸缩性。
在许多案例中,最终一致性是可以继承的,因为没有要求严格的数据一致性。比方,域名系统(DNS)就是基于一个最终一致性模子。许多中间缓存包罗 DNS 条目。如果某个人更新了一个 DNS 条目,这些条目不会被立即更新,而是在当地条目标缓存超时之后才做 DNS 查询。由于 DNS 条目标更新并不频仍,为每个名称分析执行新的 DNS 查询是一种太过利用,而且会成为网络性能的主要瓶颈。因此,在 DNS 中有一条过期的条目对于用户来说是可以容忍的。同样地,在许多别的现实场景中,我们也会这么做。我们将实施别的办理方法来检测这类过期数据大概不一致性,并在当时接纳适当的步伐,而不是灰心地使所有利用都完全一致,从而导致性能严重受损。
在我们的分布式数据存储场景中,一致性的级别取决于我们正在实现的案例。让我们再详细相识下各种一致性级别,从最强的一致性到最弱的一致性。
严格的可序列化
在严格的可序列化中,多个对象利用应该在所有副本中原子性地发生,同时保持实时顺序。实时顺序意味着,对于每个人都共享的全球时钟来说,客户端执行利用的顺序相同。这些分组的对象利用体现单独的事务。
这是实现 ACID 事务的隔离性方面所必须的。因此,如果我们需要工作负载具有 ACID 事务中的范例行为,那么在我们的分布式数据存储中就需要严格的可序列化。
线性化
在这种一致性模子中,单个对象在所有副本中的利用应该是原子的。当一个客户端看到在一个副本中完成了一个利用,任何毗连到别的副本中的客户端应该看到相同的利用。别的,发生在对象上的利用的顺序,所有客户端看到的顺序应该相同,类似于相同的实时排序。
什么时候需要这种模子?假设我们有三个客户端或进程。进程 A 向数据存储写入了一个对象值。进程 B 吸收到一些外部事件,提示它读取前面所说的对象值。在读取这个值之后,它向进程 C 发送了一条消息也去读取这个值并举行决定。进程 B 认为,它读取的对象值至少是它所拥有的最新值,而不是一个比力旧的值。为了确保这种行为,分布式数据存储必须提供线性化一致性包管,从而确保进程 A 所做的更新会立即被所有别的副本同时看到。
纵然我们设置 Cassandra 数据库为使用基于仲裁的读 / 写方案的强一致性,它也不能提供线性化包管,因为这些更新在副本中不是原子化发生的。为了拥有线性化,我们必须使用 Cassandra 中轻量化的事务支持。
顺序一致性
在顺序一致性中,一个进程对数据存储所做的利用也会在别的进程中以相同顺序发生。别的,所有利用的顺序都会与每个进程利用的相对顺序一致。根本上,它在利用的整体顺序中保存了进程级的顺序。
因果一致性
在因果一致性中,任何潜在的因果相关的利用都应该以相同的顺序对所有进程可见。简单的说,如果你基于一个先前观察到的单独利用执行了一个利用,那么对于别的进程,这些利用的顺序也应该相同。
为了满意因果一致性,应该支持以下行为:


  • 读取自己的写入:一个进程应该可以大概立即读取其之前写入利用所做的修改。
  • 单调读取:一个进程对一个对象的读取利用应该始终看到相同的大概一个较新的值。根本上,读取利用无法回退并看到一个比力旧的值。
  • 单调写入:执行写入利用的进程应该确保别的进程按其相对顺序看到这些写利用。
  • 写入跟随读取:一个进程根据先前的写利用写入的值 v1,将新值 v2 写入到对象。在这种情况下,每个人都应该先看到这个对象的 v1 值,然后才是 v2 值。
这是一个非常有用的一致性模子,适用于许多实际的应用步伐。比方,我们以一个由分布式数据存储支持的社交媒体网站为例。我们有并发用户与网站交互,更新他们的个人资料状态大概品评某个人的个人资料状态。用户 Anne,刚遇到了一个小事故,将状态设置为“遇到一个小事故,等候 X 光查抄效果!”。她刚更新了这条状态,她就得到了她的查抄效果,没有骨折。因此她将她的状态更新为“好消息:没有骨折!”。Bob 看到了 Anne 的最后一条消息,复兴她“太好了!:)”。
在上述场景中,如果我们的数据存储提供至少因果一致性,所有别的用户会以正确的顺序看到来自 Anne 和 Bob 的消息。但是如果数据存储没有提供因果一致性,有大概别的人会看到 Anne 的第一条消息和 Bob 的消息,而没有看到 Anne 的第二条更新。那种情况就变得有点儿希奇,似乎 Bob 在对 Anne 的不幸感到幸灾乐祸,而实在不是这样的。因此,我们需要在类似这样的场景中具有因果一致性。
所以假设我们有一个具有因果一致性的数据存储,在相同情况下,假设 Tom 将他的状态更新为“我刚刚得到了我的第一辆车!”,就在 Anne 更新她的第一条状态之前。网站的一些用户在 Anne 的第一条信息之后看到他的状态。这种情况没有问题,因为 Tom 的更新发生在 Anne 的更新之前或之后在现实生活中并不重要。它们之间没有接洽,即 Tom 的行为不是由 Anne 的行为导致的。别的没有因果关系的利用以最终一致性的行为利用。
一个支持因果一致性的分布式数据存储就是 MongoDB。它是基于 Lamport 逻辑锁实现的。
最终一致性
在一般的最终一致性中,如果不再向数据存储中写入数据,则数据存储中的所有副本都要聚归并最终告竣一致。它不会提供任何别的包管,比如在最终值稳定之前的因果一致性。
实际上,这种一致性模子也适用于这样的场景:只有并发值更新,但这些值更新之间没有接洽;用户并不关心中间值,只关心最终得到的稳定值。比方,以一个为每个都会发布当前气温的网站为例。这些值会不时变革。在某个时间点,一些用户大概会检察最新的气温值,而别的用户的值还没有更新。然而,最终,这个网站的所有用户都会得到更新。因此,存储这些值的分布式数据库的大概的流传延迟并不是一个大问题,只要最终所有的用户都会看到相同的气温值。
有关事务一致性模子的更多深入信息,请检察文末的资源章节。
现在,我们对于事务处理处罚和一致性模子相关的方面有了一些根本的明确。当你在任何分布式处理处罚情况,比方 MSA,工作时,这种明确就很有用。现在,我们来看看如安在 MSA 中举行数据建模。
微服务架构中的数据建模

微服务的一个根本需求是高内聚和低耦合。这是自然需要的,因为一个开发团队的组织布局也会围绕这个理念构建。将会有单独的团队负责微服务,他们需要独立于别的团队的机动性和自由度。这意味着,他们可以制止在设计和实现的内部细节方面与别的团队举行任何不须要的同步。
有了这些需求,微服务就不应该共享数据库。如果每个微服务不能拥有其自己的数据库,那么体现这些微服务需要被归并。
下面展示了一个电商后端的大概的微服务设计。

这里,我们用系统每个部门自己的微服务举行管理。这看起来很不错,直到我们需要处理处罚事务。一个范例的利用包罗创建一个用户订单,包罗一组产物。使用库存服务(inventory service)查抄过这些产物的可用性,在订单完成之后,库存会更新,淘汰那些产物的可用库存。在一个范例的单体应用中,你可以在单个 ACID 事务中执行如下利用。
SQL
  1. BEGIN TRANSACTION  CHECK INVENTORY OF PRODUCT 1 FOR 5 ITEMS  CHECK INVENTORY OF PRODUCT 2 FOR 10 ITEMS  CREATE ORDER  ADD 5 ITEMS OF PRODUCT 1 TO ORDER  ADD 10 ITEMS OF PRODUCT 2 TO ORDER  DECREMENT INVENTORY OF PRODUCT 1 BY 5 ITEMS  DECREMENT INVENTORY OF PRODUCT 2 BY 10 ITEMS  INSERT PAYMENT END TRANSACTION
复制代码
在这个方案中,我们确信数据存储在数据利用之后会保持一致的状态。但现在,我们如何使用我们的微服务对上述利用举行建模?大概想到的一种办理方案如下。

在这里,一个协调器服务“Admin”通过调用每个服务的利用创建了一个服务编排。如果所有利用都没有问题地执行,这是可行的。但很有大概,这个流程中的某一步大概失败,比方当用户没有足够的额度大概网络通信失败时发生应用步伐错误。比方,如果这个流程因为用户管理服务对支付处理处罚服务不可用而导致失败,步调 4 就会失败。但此时,我们已经创建了一个订单并更新了库存。所以,现在我们的系统中出现了不一致的状态,我们的库存陈诉的商品数量少了,但是没有人购买它们!这里显着的问题是,我们不是在一个单独的事务中执行这些利用的。在单独的事务中,如果一步失败,所有的利用都会回滚,系统会保持一致性。
我们的问题有什么大概的办理方案吗?最简单的办理方案是回归单体化方案,将所有的利用放到单个服务和单个数据库中,所有的利用在一个当地事务中执行。但是在这种情况下,假设我们已经决定这个大型单体应用步伐不能扩展,我们必须将它分解成单独的微服务。在那种情况下,对于我们的事务问题,就只剩下一个基于 2PC 的方案。我们可以使用诸如 WS-TX 或 Ballerina 的分布式事务功能来执行一个在网络服务之间基于 2PC 的全局事务。如果你想要在你的事务中拥有 ACID 包管,那么相似的方案是唯一的选择。然而,这种方案应该审慎使用,因为范例的 2PC 缺点(比方在后端数据库中增加锁定时间)仍然存在。这些缺点在微服务情况会因为额外的网络通信颠簸而增加。
然而,大多数现实生活中的工作流并不需要 ACID 包管,因为错误的利用可以使用相反的利用来逆转。因此,在我们的订单处理处罚工作流中,如果某件事堕落了,可以对已经完成的利用执行赔偿利用,并回滚整个事务。这些利用包罗将付款额度退回到用户的信用卡,通过增加回订单中的产物数量来更新产物库存,然后将订单记载更新为已取消。
我们的电子商务后端场景实际上不能仅仅建模为单个数据库事务,因为处理处罚支付的利用是使用一个外部支付网关完成的,这个网关不是一个当地的或全局的(2PC)数据库事务。然而,这种情况有一个例子,就是基于全局事务的 2PC。在这种 2PC 场景中,全局事务的最后一个加入者不需要同时实现准备 prepare 和提交 commit 两个阶段,而是单独的准备 prepare 利用就足够用来执行它的利用。这就是所谓的最后资源提交优化。而且,只有在这种特定的场景下,工作流中的任何别的地方的这种范例的加入者都不大概有全局事务。
因此,现在我们决定,我们不需要通过 2PC 得到的数据具有严格的一致性,我们可以稍后再办理任何出现的问题。让我们来看下这个工作流的一个大概的执行。

这里,工作流在步调 4 失败。从当时起,admin 服务应该以先前调用的服务的相反的顺序执行一系列赔偿利用。但是这里有一个潜在的问题。如果 admin 服务在规复利用时遇到暂时网络问题之类的异常该怎么办?我们又遇到了一个数据不一致的问题,总体回滚没有完成,而且我们无法知道我们所做的上一个利用是什么以及之后如何修复它。
处理处罚这个问题的一种方法是记载 admin 服务所做的利用。类似如下:


  • TX1: CHECK INVENTORY
  • TX1: CREATE ORDER
  • TX1: UPDATE INVENTORY
  • TX1: PROCESS PAYMENT - FAILED
  • TX1: MARK ORDER AS CANCELLED
  • TX1: UPDATE INVENTORY - INCREMENT STOCK COUNTS
因此,管理服务可以跟踪已执行的利用,以及尚未执行的利用。但是,我们也必须思量当像这样处理处罚事件日志时,大概发生的边沿情况。这个 admin 服务和它的日志独立于别的的远程服务利用,因此这些交互自己不是以事务的方式工作的。有如下变革:


  • admin 服务执行库存服务来规复库存数量(通过增加数量)
  • admin 服务更新日志,“TX1: UPDATE INVENTORY - INCREMENT STOCK COUNTS”
如果上面的第一个利用执行,然后服务在第二个利用之前瓦解了该怎么办?当 admin 服务再次继承其利用时,它会认为它没有举行库存规复利用,会再次执行第一个利用。这会导致库存数据错误,因为它增加了两次库存数目,这很糟糕!这是我们常常在分布式系统中看到的至少有一次交付的情况。一个常见的处理处罚这个问题的方案是将我们的利用建模为幂等的。也就是说,纵然相同的利用执行了许多次,它不会导致任何损害,目标系统的状态会是相同的。
但是我们的库存回滚利用不是幂等的,因为它不是设定一个特定的值,而是增加目标系统中已经存在的值。因此,你不能重复这些利用。我们可以通过直接设置我们下订单之前的库存数量,来使这成为一个幂等的利用。但是,由于我们的事务是在微服务架构中建模的,它不会提供任何你在 ACID 事务(比方,严格的序列化一致性级别)中可以大概发现的隔离属性。也就是说,当我们的事务执行时,另一个用户大概也在创建另一个订单,涉及相同的产物,会修改相同的库存记载。因此,这两个事务的利用大概重叠,导致出现不一致的情况。
因此,实际上,不但是在事务回滚的情况下,甚至在两个并发乐成的事务中,由于缺乏隔离性,一个幂等利用大概导致数据丢失更新的效果。让我们来检察下面两个事务利用的时间线。

这里有两个事务 TX1 和 TX2。它们都为产物“P1”创建订单,P1 的库存初始值是 100。TX1 将创建一个包罗 10 个产物的订单,而 TX2 会创建一个包罗 20 个产物的订单。正如我们从上面的序列图中所见,查抄库存和更新库存在这两个事务中不是原子性发生的,而是交织的,最终 TX2 的库存更新掩盖了 TX1 的库存更新。因此,最终 P1 的产物数量是 80,而它本应该是 70,因为两个事务总共购买了 30 个产物。所以现在库存数据库的数据错误地显示了实际可用的库存。
因此,我们因为这两个并发进程没有被恰当地隔离而造成了一个竞争条件。修复这个情况的一个方案是,通过一个减法利用来修正库存的值,比方 decrementInventoryStockCount(product, offset), 相对于数据库中现存的值。因此,可以在目标服务中使用单个 SQL 利用大概单个当地执行的事务来使这个利用是原子性的。
因此,通过这次更新,上面的交互可以用如下方式重写。

正如我们现在所见,随着淘汰库存中的数量,我们最终的数量将是一致的和正确的。
注意:我们仍会有一个差异的异常情况,在查抄了初始的库存数量后,在查询时,如果别的事务已经购买了所有的库存,则产物库存大概为空。这可以简单地作为一个业务流程来处理处罚,我们回滚这个利用,数据仍会保持一致。
有时候,由于我们在微服务通信中的事务隔离问题,不能使数据利用是幂等的。这意味着,如果我们不能确保一个远程微服务是否执行了一个利用,我们不能盲目地重新在一个事务中执行这个利用。这通过给微服务调用的利用设置一个唯一的事务 ID 来办理,因此目标微服务将用这个 ID 来创建一个执行的事务的汗青记载。在这种方式中,对于每个微服务利用调用,可以执行一个当地事务来查抄汗青记载,看看这个事务是否已经执行过。如果还没有执行过,就会在当地事务中执行数据库利用,更新事务汗青表。下面的代码展示了 decrementInventoryStockCount 利用在库存服务中使用上述战略的一种大概实现。
C
  1. function decrementInventoryStockCount(txid, pid, offset) {   transaction {       tx_executed = check transaction table record where id=txid       if not tx_executed {           prod_count = select count from inventory where product=pid           prod_count += offset           set count=prod_count in inventory where product=pid           insert to transaction table with id=txid       }   }}
复制代码
微服务和消息

所以现在,我们找到了一种一致的方法来在一系列微服务中执行我们的事务,拥有最终全有或全无的包管。在这个流程中,我们仍然必须维护我们的事件日志,并在一个可靠的长期化存储中更新它。如果运行统筹工作的协调服务出现故障,另一个实体必须触发它来查抄事件日志并完成任何规复利用。如果我们已经有一些中间件可以用来提供服务之间的可靠通信,来资助完成这项任务,那就太好了。
这就是事件驱动架构(EDA)有用的地方。这可以使用一个消息署理来创建微服务之间的通信。使用这种模式,我们可以确保,如果我们乐成向指向某个服务的消息代剃头送了一条消息,它会在某个时候乐成地被送到预期的收件人。这个包管使得我们的别的进程可以大概更容易建模。别的,消息署理的异步通信模子,允许同时读写,由于比力低的开销和往返调用的等候时间,因此提供了更好的性能。异常处理处罚也更简单,因为纵然目标服务挂了,消息署理也会保存消息并在目标节点可用时通报它们。别的,它可以对多个服务实例举行失败重试和负载均衡请求等别的利用。这种模式也鼓励了服务之间的松耦合。通信通过队列 / 主体举行,而且生产者和消费者并不需要明确地相互相识。Saga 模式 在实现微服务中的事务时遵循了这些一般准则。
实现这种模式有两种协调战略:编排(choreography)和统筹(orchestration)。
编排
在这种方案中,这些服务自己是知道利用流的。在将初始消息被发送到一个服务利用之后,它会生成下一条要发送到下一个服务利用的消息。服务需要对事务流有明确的相识,会导致服务之间更多的耦合。下图展示了将事务工作流实现为一个编排时,服务和队列之间的范例交互。

这里,我们可以看到,流程从客户端开始,通过其输入消息队列发送初始消息到第一个服务。在其业务逻辑中,它可以在一个用服务界说的当地事务中执行与数据有关的利用。在利用完成后,整个工作流会向编排中下一个服务的请求队列中增加一条消息。通过这种方式,整个事务上下文将通过这些消息流传到每个服务,直到事务完成。
如果工作流中的某个服务出现故障,我们需要回滚整个事务。为此,从发生故障的服务开始,它会清空它的资源,而且通过一个赔偿队列向之前刚刚执行的服务发送一条消息。这将移动上一步的执行,做一些赔偿利用往返滚其当地事务所做的变更,而且重复接洽先前服务的利用来执行赔偿利用。在这种情况下,这个异常处理处罚链会到达第一个服务,这个服务最终会发送一条消息到响应队列。这是毗连到客户端,通知发生了异常,而且已经使用赔偿利用乐成回滚了整个事务。
正如同步服务调用方案所示,当在各自的服务中执行当地事务时,我们应该维护一个事务汗青表来确保我们不会在服务收到重复消息时重复执行当地利用。别的,为了不丧失工作流的一连性,服务应该在数据库事务完成而且下一条消息被添加到下一个服务的请求队列中之后,才确认来自其请求队列中的消息。这个流程确保我们不会丢失任何消息,而且整个事务将通过继承执行或回滚所有利用来完成执行。
统筹
在这种协调方案中,我们有一个专门的协调器服务,它会按顺序调用别的服务。协调器服务和别的服务之间的通信会通过请求和响应队列完成。这种模式和我们电商场景中的“Admin”服务类似。唯一的变革是使用消息举行通信。
协调器服务和别的服务之间的异步通信允许它将事务过程建模为一个状态机,此中使用服务完成的每一步都可以更新状态机。这个状态机应该在一个数据库中长期化,以便从协调服务的任何以障中规复。下图展示了使用消息驱动战略的统筹协调方案是如何设计的。

与编排方案相比,统筹方案的服务之间的耦合更少。这是因为工作流是被协调服务驱动的,特定时刻的完整状态是在谁人服务自己持有的。但是,这里的服务也不是完全独立的,因为它们的请求和响应是绑定到特定的队列的,固定的生产者和固定的消费者使用这些队列。因此,现在更难使用这些服务作为通用服务。
当我们的利用数量比力少时,基于编排的协调是可行的。对于复杂的利用,基于统筹的方案在对利用举行建模时更机动。
在实现这种战略时,在开发框架中抽象出通信、状态机的长期化等细节是很重要的。否则,开发者将写更多代码来实现事务处理处罚,而不是焦点业务逻辑。别的,如果一个范例的开发人员总是重新实现这种模式,会更容易堕落。
为微服务选择一种事务模子

在我们实现事务时使用的任何技能中,我们需要明确每种方案给出的数据一致性包管。然后我们必须与我们的业务需求交织查抄,看看什么是最适合我们的。下面可以用作一般指南。


  • 2PC: 如果微服务是使用差异的编程语言 / 框架 / 数据库以及来自差异公司的开发团队创建的,那就不大概将所有利用组合到单个服务中。别的,它还要求严格的数据一致性,在这种情况下,任何数据隔离问题(如脏读)都不能与业务需求相关。
  • 基于赔偿的事务:这是使用一个事务协调机制来跟踪事务中的每一个步调,如果出现故障,执行赔偿利用往返滚动作。这通常应该会使用幂等的数据利用或互换式更新,来处理处罚消息重复场景。你的业务需求应该可以大概处理处罚最终一致性行为(如脏读)。
  • 整合服务:由于这种方案的性能问题和可伸缩性,在 2PC 中使用这种方案是不能容忍的,但业务需要严格的数据一致性。在这种情况下,我们应该将相关的功能一起放到它们各自的单个服务中并使用当地事务。
总结

在本文中,我们研究了事务处理处罚的根本知识,从 ACID 包管到使用 BASE 放松数据一致性包管,以及 CAP 定理如安在一个分布式系统中界说数据存储的权衡。然后,我们分析了在分布式数据存储和一般的分布式进程中的差异级别的数据一致性。这些数据一致性问题直接适用于 MSA 中的数据建模,在 MSA 中我们需要将各个独立的服务组合起来执行一个全局的事务。
在根据业务要求选择一种选项时,我们检察了每种方案的优势和权衡,并查抄了一般准则。
作者先容
Anjana Fernando :WSO2 公司董事
原文链接
https://dzone.com/articles/practical-transaction-handling-in-microservice-arc
点这里:2020Python高薪实战学习大合集**
[拿走不谢!Python 3.9 官方中文文档,限时领!] (http://dwz.date/dE6v)
[限时!速领!14张高清Python速查表,效率提升必备!] (http://dwz.date/dE6w)
[GitHub标星3W+,80个Python案例,带你轻松玩转Python学习!] (http://dwz.date/dE64)

来源:https://blog.csdn.net/weixin_43507410/article/details/112007479
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题

专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )