【转】微服务架构下的数据一致性保证(一)

今天分享第一篇,主要内容包括:

 

1.传统使用本地事务和分布式事务保证一致性。

2.传统分布式事务不是微服务中一致性的最佳选择。

3.微服务架构中应满足数据最终一致性原则。

4.微服务架构实现最终一致性的三种模式。

5.对账是最后的终极防线。

一、传统使用本地事务和分布式事务保证一致性

 

传统单机应用一般都会使用一个关系型数据库,好处是应用可以使用 ACID transactions。为保证一致性我们只需要:开始一个事务,改变(插入,删除,更新)很多行,然后提交事务(如果有异常时回滚事务)。更进一步,借助开发平台中的数据访问技术和框架(如Spring),我们需要做的事情更少,只需要关注数据本身的改变。

 

随着组织规模不断扩大,业务量不断增长,单机应用和数据库已经不足以支持庞大的业务量和数据量,这个时候需要对应用和数据库进行拆分,就出现了一个应用需要同时访问两个或两个以上的数据库情况。开始我们用分布式事务来保证一致性,也就是我们常说的两阶段提交协议(2PC)。

 

本地事务和分布式事务现在已经非常成熟,相关介绍很丰富,此处不多作讨论。

 

二、传统分布式事务不是微服务中一致性的最佳选择

 

首先,对于微服务架构来说,数据访问变得更加复杂,这是因为数据都是微服务私有的,唯一可访问的方式就是通过API。这种打包数据访问方式使得微服务之间松耦合,并且彼此之间独立非常容易进行性能扩展。

其次,不同的微服务经常使用不同的数据库。应用会产生各种不同类型的数据,关系型数据库并不一定是最佳选择。

 

例如,某个产生和查询字符串的应用采用Elasticsearch的字符搜索引擎;某个产生社交图片数据的应用可以采用图数据库,例如,Neo4j;

基于微服务的应用一般都使用SQL和NoSQL结合的模式。但是这些非关系型数据大多数并不支持2PC。

可见在微服务架构中已经不能选择分布式事务了。

 

三、微服务架构中应满足数据最终一致性原则 

依据CAP理论,必须在可用性(availability)和一致性(consistency)之间做出选择。如果选择提供一致性需要付出在满足一致性之前阻塞其他并发访问的代价。这可能持续一个不确定的时间,尤其是在系统已经表现出高延迟时或者网络故障导致失去连接时。

 

依据目前的成功经验,可用性一般是更好的选择,但是在服务和数据库之间维护数据一致性是非常根本的需求,微服务架构中选择满足最终一致性

当然选择了最终一致性,就要保证到最终的这段时间要在用户可接受的范围之内。

 

那么我们怎么实现最终一致性呢?

 

四、微服务架构实现最终一致性的三种模式

 

从一致性的本质来看,是要保证在一个业务逻辑中包含的服务要么都成功,要么都失败。那我们怎么选择方向呢?保证成功还是保证失败呢?

我们说业务模式决定了我们的选择。实现最终一致性有三种模式:可靠事件模式、业务补偿模式、TCC模式。

 

1) 可靠事件模式

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

1. 如订单服务创建一个待支付的订单,发布一个“创建订单”的事件。

 

2.支付服务消费“创建订单”事件,支付完成后发布一个“支付完成”事件。

3.订单服务消费“支付完成”事件,订单状态更新为待出库。

从而就实现了完成的业务流程。

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

 

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

可靠事件投递定义为:

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

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

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

 

2) 补偿模式

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

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

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

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

 

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

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

 

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

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

 

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

 

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

 

 3) TCC模式(Try-Confirm-Cancel)

 

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

例如从服务有2个:

1) Try:完成所有业务检查

预留必须业务资源

 

2) Confirm:真正执行业务

不作任何业务检查

只使用Try阶段预留的业务资源

Confirm操作满足幂等性

 

3) Cancel:

释放Try阶段预留的业务资源

Cancel操作满足幂等性

整个TCC业务分成两个阶段完成。

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

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

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

 

五、对账是最后的终极防线

如果有些业务由于瞬时的网络故障或调用超时等问题,通过上文所讲的3种模式一般都能得到很好的解决。但是在当今云计算环境下,很多服务是依赖于外部系统的可用性情况,在一些重要的业务场景下还需要周期性的对账来保证真实的一致性。比如支付系统和银行之间每天日终是都会有对账过程。

转:https://mp.weixin.qq.com/s?__biz=MzI5MDEzMzg5Nw==&mid=2660392782&idx=1&sn=d28e43bf6f7cf140eed9fffcf2f29e86&scene=1&srcid=0811qaoc12E47x6vDVNkynBO#rd


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐