微服务的一致性问题

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

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

 

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

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

Atomicity:原子性(要么都做,要么都不做)

Consistency:一致性(数据库只有一个状态,不存在未确定状态)

Isolation:隔离性(事务之间互不干扰)

Durability: 永久性(事务一旦提交,数据库记录永久不变)

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

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

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

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

C:Consistency,一致性,所有数据变动都是同步的。

A:Availability,可用性,即在可以接受的时间范围内正确地响应用户请求。

P:Partition tolerance,分区容错性,即某节点或网络分区故障时,系统仍能够提供满足一致性和可用性的服务

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

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

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

BA:Basically Available,基本可用。

S:Soft State,软状态,状态可以有一段时间不同步。

E:Eventually Consistent,最终一致,最终数据是一致的就可以了,而不是时时保持强一致。

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

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

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

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

2、二阶段提交协议

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)的提交方法时,事务的提交分为两个阶段实行。

2.1、第一阶段(准备阶段)

 

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

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

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

 

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

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

2.3、二阶段提交的利弊

优点

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

缺点

TM 通过 XA 接口与各个 RM 之间进行数据交互,从第一阶段的准备阶段,业务所涉及的数据就被锁定,并且锁定跨越整个提交流程。在高并发和涉及业务模块较多的情况下对数据库的性能影响较大。

二阶段是 反可伸缩模式 的,业务规模越大,涉及模块越多,局限性越大,系统可伸缩性越差。

在技术栈比较杂的分布式应用中,存储组件有很多 不支持 XA 协议。

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

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

1、可靠事件模式

 

可靠事件模式属于事件驱动架构,当某件重要事情发生时,例如更新一个业务实体,微服务会向消息代理发布一个事件。消息代理会向订阅事件的微服务推送事件,当订阅这些事件的微服务接收此事件时,就可以完成自己的业务,也可能会引发更多的事件发布。

这个过程可能导致出现不一致的地方在于:某个微服务在更新了业务实体后发布事件却失败;虽然微服务发布事件成功,但是消息代理未能正确推送事件到订阅的微服务;接受事件的微服务重复消费了事件。

 

可靠事件模式在于保证可靠事件投递避免重复消费,可靠事件投递定义为

a)每个服务原子性的业务操作和发布事件

b)消息代理确保事件传递至少一次。

 

避免重复消费要求服务实现幂等性,如支付服务不能因为重复收到事件而多次支付。

 

2补偿模式

 

为了描述方便,这里先定义两个概念:

 

业务异常:业务逻辑产生错误的情况,比如账户余额不足、商品库存不足等。

技术异常:非业务逻辑产生的异常,如网络连接异常、网络超时等。

 

补偿模式使用一个额外的协调服务来协调各个需要保证一致性的微服务,协调服务按顺序调用各个微服务,如果某个微服务调用异常(包括业务异常和技术异常)就取消之前所有已经调用成功的微服务。

 

补偿模式建议仅用于不能避免出现业务异常的情况,如果有可能应该优化业务模式,以避免要求补偿事务。如账户余额不足的业务异常可通过预先冻结金额的方式避免,商品库存不足可要求商家准备额外的库存等。

我们通过一个实例来说明补偿模式,一家旅行公司提供预订行程的业务,可以通过公司的网站提前预订飞机票、火车票、酒店等。

 

假设一位客户规划的行程是,(1)上海-北京6月19日9点的某某航班,(2)某某酒店住宿3晚,(3)北京-上海6月22日17点火车。在客户提交行程后,旅行公司的预订行程业务按顺序串行的调用航班预订服务、酒店预订服务、火车预订服务。最后的火车预订服务成功后整个预订业务才算完成。

如果火车票预订服务没有调用成功,那么之前预订的航班、酒店都得取消。取消之前预订的酒店、航班即为补偿过程。

需要注意的是酒店的取消预订、航班的取消预订同样不能保证一定成功,所以补偿过程往往也同样需要实现最终一致性,需要保证取消服务至少被调用一次和取消服务必须实现幂等性。

 

我们应该尽可能通过设计避免采用补偿方式,比如上面的例子中,在预订火车票失败的时候可以提示客户更改其他的时间。

 

3TCC模式(Try-Confirm-Cancel

一个完整的TCC业务由一个主业务服务和若干个从业务服务组成,主业务服务发起并完成整个业务活动,TCC模式要求从服务提供三个接口:Try、Confirm、Cancel。

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

 

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

 

需要注意的是第二阶段confirm或cancel操作本身也是满足最终一致性的过程,在调用confirm或cancel的时候也可能因为某种原因(比如网络)导致调用失败,所以需要活动管理支持重试的能力,同时这也就要求confirm和cancel操作具有幂等性。

幂等性

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

在事务提交失败会重复提交达到最大重复次数时返回失败的 设计中  保持幂等性是尤为重要的

 

 

猜你喜欢

转载自www.cnblogs.com/itxiaok/p/9453397.html