从 0 开始的微服务架构:(四)如何保障微服务架构下的数据一致性

虽然已经红了很久,但是“微服务架构”正变得越来越重要,也将继续火下去。各个公司与技术人员都在分享微服务架构的相关知识与实践经验,但我们发现,目前网上的这些相关文章中,要么上来就是很有借鉴意义的干货,要么就是以高端的专业术语来讲述何为微服务架构。就是没有一个做到成熟地将技术传播出来,同时完美地照顾“初入微服务领域人员”,从 0 开始,采用通俗易懂的语言去讲解微服务架构的系列。所以,我们邀请青柳云的苏槐与 InfoQ 一起共建微服务架构专题“Re:从 0 开始的微服务架构”,为还没有入门该领域的技术人员开路,也帮助微服务架构老手温故知新。

专题文章传送

这是专题的第四篇文章,我们来了解一下如何保障微服务架构下的数据一致性。

随着微服务架构的推广,越来越多的公司采用微服务架构来构建自己的业务平台。就像前边的文章说的,微服务架构为业务开发带来了诸多好处的同时,例如单一职责、独立开发部署、功能复用和系统容错等等,也带来一些问题。

例如上手难度变大,运维变得更复杂,模块之间的依赖关系更复杂,数据一致性难以保证,等等。但是办法总是比问题多,本篇文章就来介绍一下我们是如何保障微服务架构的数据一致性的。

微服务架构的数据一致性问题

以电商平台为例,当用户下单并支付后,系统需要修改订单的状态并且增加用户积分。由于系统采用的是微服务架构,分离出了支付服务、订单服务和积分服务,每个服务都有独立数据库做数据存储。当用户支付成功后,无论是修改订单状态失败还是增加积分失败,都会 造成数据的不一致。

为了解决例子中的数据一致性问题,一个最直接的办法就是考虑数据的 强一致性。那么如何保证数据的强一致性呢?我们从关系型数据库的 ACID 理论说起。

ACID

关系型数据库具有解决复杂事务场景的能力,关系型数据库的事务满足 ACID 的特性。

  • Atomicity:原子性(要么都做,要么都不做)
  • Consistency:一致性(数据库只有一个状态,不存在未确定状态)
  • Isolation:隔离性(事务之间互不干扰)
  • Durability: 永久性(事务一旦提交,数据库记录永久不变)

具有 ACID 特性的数据库支持数据的强一致性,保证了数据本身不会出现不一致。

然而微服务架构下,每个微服务都有自己的数据库,导致微服务架构的系统不能简单地满足 ACID,我们就需要寻找微服务架构下的数据一致性解决方案。

微服务架构的系统本身是一种分布式系统,而本文讨论的问题其实也就是分布式事务之数据一致性的问题,我们来聊聊分布式系统的 CAP 理论和 BASE 理论。

CAP

CAP 是指在一个分布式系统下, 包含三个要素:Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),并且 三者不可得兼。

  • C:Consistency,一致性,所有数据变动都是同步的。
  • A:Availability,可用性,即在可以接受的时间范围内正确地响应用户请求。
  • P:Partition tolerance,分区容错性,即某节点或网络分区故障时,系统仍能够提供满足一致性和可用性的服务。

关系型数据库 单节点 保证了数据强一致性(C)和可用性(A),但是却无法保证分区容错性(P)。

然而在分布式系统下,为了保证模块的分区容错性(P),只能在数据强一致性(C)和可用性(A)之间做平衡。具体表现为在一定时间内,可能模块之间数据是不一致的,但是通过自动或手动补偿后能够达到最终的一致。

BASE

BASE 理论主要是解决 CAP 理论中分布式系统的可用性和一致性不可兼得的问题。BASE 理论包含以下三个要素:

  • BA:Basically Available,基本可用。
  • S:Soft State,软状态,状态可以有一段时间不同步。
  • E:Eventually Consistent,最终一致,最终数据是一致的就可以了,而不是时时保持强一致。

BASE 模型与 ACID 不同,满足 CAP 理论,通过 牺牲强一致性来保证系统可用性。由于牺牲了强一致性,系统在处理请求的过程中,数据可以存在短时的不一致。

系统在处理业务时,记录每一步的临时状态。当出现异常时,根据状态判断是否继续处理请求或者退回原始状态,从而达到数据的最终一致。

例如,在上面的案例中,支付成功,订单也成功,但增加积分失败,此时,不应回滚支付和订单,而应通过一些 补偿方法 来让积分得以正确地增加。后面会讲到具体的实现方法。

在分享我们的分布式事务实践方案之前,先看看早期解决分布式事务问题的二阶段提交协议。

二阶段提交协议

X/Open DTP(Distributed Transaction Process)是一个分布式事务模型,此模型主要使用二阶段提交(2PC,Two-Phase-Commit)来保证分布式事务的完整性。在这个模型里面,有三个角色:

  • AP:Application,应用程序,业务层。
  • RM:Resource Manager,资源管理器,关系型数据库或支持 XA 接口(XA 规范是 X/Open 组织定义的分布式事务规范)的组件。
  • TM: Transaction Manager ,事务管理器,负责各个 RM 的提交和回滚。

当应用程序(AP)调用了事务管理器(TM)的提交方法时,事务的提交分为两个阶段实行。

第一阶段(准备阶段)

(点击放大图像)

TM 通知所有参与事务的各个 RM,给每个 RM 发送 prepare 消息。

RM 接收到消息后进入准备阶段后,要么直接返回失败,要么创建并执行本地事务,写本地事务日志(redo 和 undo 日志),但是 不提交(此处只保留最后一步耗时最少的提交操作给第二阶段执行)。

第二阶段(提交 / 回滚阶段)

(点击放大图像)

TM 收到 RM 准备阶段的失败消息或者获取 RM 返回消息超时,则直接给 RM 发送回滚(rollback)消息,否则发送提交(commit)消息。

RM 根据 TM 的指令执行提交或者回滚,执行完成后释放所有事务处理过程中使用的锁(最后阶段释放锁)。

二阶段提交的利弊

优点

2PC 提供了一套完整的分布式事务的解决方案,遵循事务严格的 ACID 特性。

缺点

  • TM 通过 XA 接口与各个 RM 之间进行数据交互,从第一阶段的准备阶段,业务所涉及的数据就被锁定,并且锁定跨越整个提交流程。在高并发和涉及业务模块较多的情况下 对数据库的性能影响较大。
  • 二阶段是 反可伸缩模式 的,业务规模越大,涉及模块越多,局限性越大,系统可伸缩性越差。
  • 在技术栈比较杂的分布式应用中,存储组件有很多 不支持 XA 协议。

二阶段的诸多弊端,导致分布式系统下无法直接使用此方案来解决数据一致性问题,但它提供了解决分布式系统下数据一致性问题的思路。。

下面就通过案例来分享我们是如何保证微服务架构的数据一致性的。

可靠消息最终一致性

可靠消息最终一致性方案本质上是 利用 MQ 组件实现的二阶段提交。此方案涉及 3 个模块:

  • 上游应用,执行业务并发送 MQ 消息。
  • 可靠消息服务和 MQ 消息组件,协调上下游消息的传递,并确保上下游数据的一致性。
  • 下游应用,监听 MQ 的消息并执行自身业务。

(点击放大图像)

上游应用执行业务并发送 MQ 消息(第一阶段)

上游应用将本地业务执行和消息发送绑定在同一个本地事务中,保证要么本地操作成功并发送 MQ 消息,要么两步操作都失败并回滚。

上游应用和可靠消息之间的业务交互图如下:

(点击放大图像)

  1. 上游应用发送待确认消息到可靠消息系统
  2. 可靠消息系统保存待确认消息并返回
  3. 上游应用执行本地业务
  4. 上游应用通知可靠消息系统确认业务已执行并发送消息。
  5. 可靠消息系统修改消息状态为发送状态并将消息投递到 MQ 中间件。

以上每一步都可能出现失败情况,分析一下这 5 步出现异常后上游业务和消息发送是否一致:

上游应用执行完成,下游应用尚未执行或执行失败时,此事务即处于 BASE 理论的 Soft State 状态。

下游应用监听 MQ 消息并执行业务(第二阶段)

下游应用监听 MQ 消息并执行业务,并且将消息的消费结果通知可靠消息服务。

可靠消息的状态需要和下游应用的业务执行保持一致,可靠消息状态不是已完成时,确保下游应用未执行,可靠消息状态是已完成时,确保下游应用已执行。

下游应用和可靠消息服务之间的交互图如下:

(点击放大图像)

  1. 下游应用监听 MQ 消息组件并获取消息
  2. 下游应用根据 MQ 消息体信息处理本地业务
  3. 下游应用向 MQ 组件自动发送 ACK 确认消息被消费
  4. 下游应用通知可靠消息系统消息被成功消费,可靠消息将该消息状态更改为已完成。

以上每一步都可能出现失败情况,分析一下这 4 步出现异常后下游业务和消息状态是否一致:

通过分析以上两个阶段可能失败的情况,为了确保上下游数据的最终一致性,在可靠消息系统中,需要开发 消息状态确认 和 消息重发 两个功能以实现 BASE 理论的 Eventually Consistent 特性。

消息状态确认

可靠消息服务定时监听消息的状态,如果存在状态为待确认并且超时的消息,则表示上游应用和可靠消息交互中的步骤 4 或者 5 出现异常。

可靠消息则携带消息体内的信息向上游应用发起请求查询该业务是否已执行。上游应用提供一个可查询接口供可靠消息追溯业务执行状态,如果业务执行成功则更改消息状态为已发送,否则删除此消息确保数据一致。具体流程如下:

(点击放大图像)

  1. 可靠消息查询超时的待确认状态的消息
  2. 向上游应用查询业务执行的情况
  3. 业务未执行,则删除该消息,保证业务和可靠消息服务的一致性。业务已执行,则修改消息状态为已发送,并发送消息到 MQ 组件。

消息重发

消息已发送则表示上游应用已经执行,接下来则确保下游应用也能正常执行。

可靠消息服务发现可靠消息服务中存在消息状态为已发送并且超时的消息,则表示可靠消息服务和下游应用中存在异常的步骤,无论哪个步骤出现异常,可靠消息服务都将此消息重新投递到 MQ 组件中供下游应用监听。

下游应用监听到此消息后,在保证幂等性的情况下重新执行业务并通知可靠消息服务此消息已经成功消费,最终确保上游应用、下游应用的数据最终一致性。具体流程如下:

(点击放大图像)

  1. 可靠消息服务定时查询状态为已发送并超时的消息
  2. 可靠消息将消息重新投递到 MQ 组件中
  3. 下游应用监听消息,在满足幂等性的条件下,重新执行业务。
  4. 下游应用通知可靠消息服务该消息已经成功消费。

通过消息状态确认和消息重发两个功能,可以确保上游应用、可靠消息服务和下游应用数据的最终一致性。

当然在实际接入过程中,需要引入 人工干预 功能。比如引入重发次数限制,超过重发次数限制的将消息修改为死亡消息,等待人工干预。

代入开篇案例,通过可靠消息最终一致性方案,第一阶段,订单状态更改之前,订单服务向可靠消息服务请求保存待确认消息。可靠消息服务保存消息并返回。

订单服务接收到返回信息后执行本地业务并通知可靠消息服务业务已执行。消息服务更改消息状态并将消息投递到 MQ 中间件。

第二阶段,积分系统监听到 MQ 消息,查看积分是否已增加,如果没有增加则修改积分,然后请求可靠消息服务。可靠消息服务接收到积分系统的请求,将消息状态更改为已完成。

到这里,已经介绍完如何通过可靠消息服务来保证数据的一致性。但由于引入了可靠消息服务和消息队列,带来了一定的 复杂性,所以,它更 适用于跨平台技术栈不统一的场景。

下面再来介绍在技术栈统一的情况下,如何通过 TCC 来解决数据一致的方法。

TCC(Try-Confirm-Cancel)

TCC 方案是二阶段提交的 另一种实现方式,它涉及 3 个模块,主业务、从业务 和 活动管理器(协作者)。

下面这张图是互联网上关于 TCC 比较经典的图示:

(点击放大图像)

第一阶段:主业务服务分别调用所有从业务服务的 try 操作,并在活动管理器中记录所有从业务服务。当所有从业务服务 try 成功或者某个从业务服务 try 失败时,进入第二阶段。

第二阶段:活动管理器根据第一阶段从业务服务的 try 结果来执行 confirm 或 cancel 操作。如果第一阶段所有从业务服务都 try 成功,则协作者调用所有从业务服务的 confirm 操作,否则,调用所有从业务服务的 cancel 操作。

在第二阶段中,confirm 和 cancel 同样存在失败情况,所以需要对这两种情况做 异常处理以保证数据一致性。

  1. Confirm 失败:则回滚所有 confirm 操作并执行 cancel 操作。
  2. Cancel 失败:从业务服务需要提供自动 cancel 机制,以保证 cancel 成功。

目前有很多基于 RPC 的 TCC 框架,但是不适用于微服务架构下基于 HTTP 协议的交互模式。我们这次只讨论基于 HTTP 协议的 TCC 实现。具体的实现流程如下:

(点击放大图像)

  1. 主业务服务调用从业务服务的 try 操作,并获取 confirm/cancel 接口和超时时间。
  2. 如果从业务都 try 成功,主业务服务执行本地业务,并将获取的 confirm/cancel 接口发送给活动管理器,活动管理器会顺序调用从业务 1 和从业务 2 的 confirm 接口并记录请求状态,如果请求成功,则通知主业务服务提交本地事务。如果 confirm 部分失败,则活动管理器会顺序调用从业务 1 和从业务 2 的 cancel 接口来取消 try 的操作。
  3. 如果从业务部分或全部 try 失败,则主业务直接回滚并结束,而 try 成功的从业务服务则通过定时任务来处理处于 try 完成但超时的数据,将这些数据做回滚处理保证主业务服务和从业务服务的数据一致。

代入开篇提到的案例,通过 TCC 方案,订单服务在订单状态修改之前执行预增积分操作(try),并从积分服务获取 confirm/cancel 预增积分的请求地址。

如果预增积分(try)成功,则订单服务更改订单状态并通知活动管理器,活动管理器请求积分模块的 confirm 接口来增加积分。

如果预增积分(try)失败,则订单服务业务回滚。积分服务通过定时任务删除预增积分(try)超时的数据。

另外如果活动管理器调用积分服务的 confirm 接口失败,则活动管理器调用积分服务 cancel 接口来取消预增积分,从而,保证订单和积分数据的最终一致性。

通过上面的对可靠消息服务和 TCC 方案的描述,我们 解决了技术栈一致和不一致的两种情况下的数据一致性问题。

但是,通常在这些核心业务上有 很多附加业务,比如当用户支付完成后,需要通过短信通知用户支付成功。

这一类业务的成功或者失败不会影响核心业务,甚至很多大型互联网平台在并高并发的情况下会主动关闭这一类业务以保证核心业务的顺利执行。那么怎么处理这类情况呢,我们来看看最大努力通知方案。

最大努力通知

最大努力通知方案涉及三个模块:

  • 上游应用,发消息到 MQ 队列。
  • 下游应用(例如短信服务、邮件服务),接受请求,并返回通知结果。
  • 最大努力通知服务,监听消息队列,将消息存储到数据库中,并按照通知规则调用下游应用的发送通知接口。

具体流程如下:

(点击放大图像)

  1. 上游应用发送 MQ 消息到 MQ 组件内,消息内包含通知规则和通知地址
  2. 最大努力通知服务监听到 MQ 内的消息,解析通知规则并放入延时队列等待触发通知
  3. 最大努力通知服务调用下游的通知地址,如果调用成功,则该消息标记为通知成功,如果失败则在满足通知规则(例如 5 分钟发一次,共发送 10 次)的情况下重新放入延时队列等待下次触发。

最大努力通知服务表示在 不影响主业务 的情况下,尽可能地确保数据的一致性。它需要开发人员根据业务来指定通知规则,在满足通知规则的前提下,尽可能的确保数据的一致,以尽到最大努力的目的。

根据不同的业务可以定制不同的通知规则,比如通知支付结果等相对严谨的业务,可以将通知频率设置高一些,通知时间长一些,比如隔 5 分钟通知一次,持续时间 1 小时。

如果不重要的业务,比如通知用户积分增加,则可以将通知频率设置低一些,时间短一些,比如 10 分钟通知一次,持续 30 分钟。

代入上面提到的支付成功短信通知用户的案例,通过最大努力通知方案,当支付成功后,将消息发送到 MQ 中间件,在消息中,定义发送规则为 5 分钟一次,最大发送数为 10 次。

最大努力通知服务监听 MQ 消息并根据规则调用消息通知服务(短信服务)的消息发送接口,并记录每次调用的日志信息。在通知成功或者已通知 10 次时,停止通知。

总结

上面通过案例详细介绍了我们解决微服务之间数据不一致问题的三种方案,下面通过一张简单的对比图,为大家选择合适的解决方案提供简单依据。

原文地址:https://www.cnblogs.com/kevinX/p/8340899.html

时间: 2024-10-11 04:42:21

从 0 开始的微服务架构:(四)如何保障微服务架构下的数据一致性的相关文章

Re:从 0 开始的微服务架构--(四)如何保障微服务架构下的数据一致性--转

原文地址:http://mp.weixin.qq.com/s/eXvoJew3bjFKzLLJpS0Otg 随着微服务架构的推广,越来越多的公司采用微服务架构来构建自己的业务平台.就像前边的文章说的,微服务架构为业务开发带来了诸多好处的同时,例如单一职责.独立开发部署.功能复用和系统容错等等,也带来一些问题. 例如上手难度变大,运维变得更复杂,模块之间的依赖关系更复杂,数据一致性难以保证,等等.但是办法总是比问题多,本篇文章就来介绍一下我们是如何保障微服务架构的数据一致性的. 微服务架构的数据一

微服务架构下的数据一致性:概念及相关模式

主页:http://www.howardliu.cn/ 博客:微服务架构下的数据一致性:概念及相关模式 从2014年开始,微服务逐渐进入大家的实现,被认为是下一代实现信息化的有效手段.设计到系统,其中绕不开的就是数据一致性,从本地事务,到后来的分布式事务,都能够有效的保证数据一致性.但是在微服务架构中,这两种方式都不是最好的选择. 1. 使用本地事务和分布式事务保证一致性 在传统的单击应用中,最简单.最直接.最普遍的会使用一个关系型数据库,通过关系型数据库的事务保证数据的一致性.这种事务有四个基

用友云服务治理平台 助力企业微服务架构落地

本文主要阐述使用微服务架构时,治理框架或者平台需要解决的主要问题,微服务落地实施过程中所遇到的关键问题和对应解决方案.同时,文章也介绍用友云旗下的微服务治理平台的核心功能和技术架构,以及微服务治理平台在用友云一些产品下的实践,下一步的发展计划和趋势. 用友云微服务治理平台由来 伴随互联网.云计算.大数据等技术的快速发展,越来越多的企业在信息化之后,将企业上云和数字化提上日程.软件架构的微服务方式重构.应用的自动化运维.容器化等需求强烈,催生出了众多的PaaS平台.同时,针对微服务,也涌现除了许多

微服务架构下的数据一致性:可靠事件模式

主页:http://www.howardliu.cn/ 博客:微服务架构下的数据一致性:可靠事件模式 在<微服务架构下的数据一致性:概念及相关模式>中介绍了在微服务中实现数据一致性的三种方式,包括可靠事件模式.业务补偿模式.TCC模式.本文重点说一下可靠事件投递. 1. 可靠事件模式 可靠事件模式属于事件驱动架构,微服务完成操作后向消息代理发布事件,关联的微服务从消息代理订阅到该事件从而完成相应的业务操作,关键在于可靠事件投递和避免事件重复消费. 可靠事件投递有两个特性:1)每个服务原子性的完

唯品会、滴滴、沪江架构师,关于微服务粒度、高可用、持续交互的实践分享交流(下)

架构师小组交流会:每期选择一个时下最热门的技术话题进行实践经验分享. 本期小组交流会邀请到了沪江黄凯.唯品会郑明华.滴滴赵伟.七牛云肖勤,对微服务粒度.高可用.持续交互展开了交流. 本期接着上期唯品会.滴滴.沪江架构师,关于微服务粒度.高可用.持续交互的实践分享交流(上)进行了交流. 第一轮:话题交流 滴滴赵伟:在整个服务,从单体服务到微服务的演进过程当中,如何去影响业务的这种正常发展? 唯品会郑明华:从单体服务到微服务的改造,有两种方式,一种是小打小闹,每次稍微改一点,这个时间会非常长,有时候

Atitit.架构设计趋势 设计模式 ---微服务架构&#160;&#160;soa

Atitit.架构设计趋势 设计模式 ---微服务架构  soa 什么是微服务架构?1 .微服务与SOA的关系 :微服务架架构师面向服务架构(SOA)的一种特定实现1 微服务与康威定律2 微服务的一些设计 断路器 幂等2 <微服务设计>([英] 纽曼(Sam Newman))3 微服务架构与实践4 什么是微服务架构? Martin Fowler认为,微服务架构是一种独立部署的软件应用设计方式.这种架构方式没有准确的定义,但是在业务能力.自动部署.端对端的整合.对语言及数据的分散控制上有着共性.

【微服务干货系列】使用微服务架构之前,你必须知道的

正如敏捷之父MartinFowler所说的那样,单体架构和微服务并非简单的二选一,两者都是模糊的定义.这就意味着大多数系统都将在一个模糊的边界区域.非常多开发团队已经认识到微服务架构比单体架构更优越.可是也有其它团队感觉到这是一种消弱生产力的负担,就像不论什么软件架构,微服务架构相同有利弊.为了能做出一个明智的选择.你必须了解这些应用并将它们运用到你特定的环境中. 微服务的"定义" 假设须要准确的给微服务下一个定义,抱歉,笔者找了非常长时间,也没有一个准确的定义,最接近微服务的定义来自

解析微服务架构(二):融入微服务的企业集成架构

上一篇文章介绍了微服务架构的起源.定义.通用特性.常见概念误区.微服务架构与SOA架构比较.微服务架构收益以及企业引入微服务架构的策略. 本文将介绍融入微服务的企业集成架构的演进,并描述交互式系统的微服务模式及相关技术决策,然后给出了一个具体的微服务架构业务应用的例子. 交互型系统(System of Engagement)与记录型系统(System of Record) 随着移动互联网的快速发展,企业除了需要提供传统核心IT系统能力之外,还需提供客户与合作伙伴友好型的以交互为重点的创新及交互式

百度“百老汇”架构师深刻透视微服务架构

首先解释这个"百老汇"=百度老年架构师活动中心.......什么是微服务首先微服务并没有一个官方的定义,想要直接描述微服务比较困难,我们可以通过对比传统WEB应用,来理解什么是微服务.传统的WEB应用核心分为业务逻辑.适配器以及API或通过UI访问的WEB界面.业务逻辑定义业务流程.业务规则以及领域实体.适配器包括数据库访问组件.消息组件以及访问接口等.一个打车软件的架构图如下: 尽管也是遵循模块化开发,但最终它们会打包并部署为单体式应用.例如Java应用程序会被打包成WAR,部署在T