分享

大型SOA架构体系里的数据一致性问题

 LZS2851 2017-02-12

大型SOA架构体系里的数据一致性问题

大规模事务处理系统如大型电子商务网站后台中,分布式事务是个绕不过去的挑战,而微服务架构的流行更加让这个问题日益突出。这表现在几个方面:首先,数据需要保证严格一致,任何订单,支付,库存等等地管理都不允许有任何错误;其次,业务逻辑常常需要多个服务的更新,因此解决“部分失效”问题是这类系统不得不面对的问题。

大型SOA架构体系里的数据一致性问题

例如图中服务A和服务B,在一次事务处理中均需要调用,那么如果服务B发生崩溃,或者网络错误,服务A应当如何处理?如果服务A发生崩溃,那么恢复后需要询问服务B是否重做刚才没有完成的事务么?

如何确保服务之间的数据一致是这类架构挑战需要解决的核心问题。根据Weikum和Vossen合著的Transactional Information Systems书中描述,有3种手段用来处理这种情形:

第一种是引入分布式事务,最常见的就是2PC两阶段提交。如下图所示:

大型SOA架构体系里的数据一致性问题

应用2PC在SOA架构体系里,并不是推荐的做法,因为这其实是违反了进行服务治理的初衷,此外,2PC本身的缺点也不可忽视,例如DTC的可用性问题会导致死锁,2PC带来的锁引发的性能开销等。服务级别2PC的协议并不统一,例如Jboss社区的Narayana就是一个实现了一系列分布式事务协议的事务处理器,目前包含XATMI,JTA,JTS,Web-Service Transactions,以及Jboss自有的REST Transactions等等一系列事务协议。

第二种称为“无状态队列事务”。采用消息队列和最终一致性来进行服务间解耦。为了确保数据的一致,一个服务必须能够在其领域所在的数据库发生变化时,原子地把该变化发布成事件。

大型SOA架构体系里的数据一致性问题

这种技术手段具备很高的性能和伸缩性,可以避免分布式事务带来的性能和可用性瓶颈。但缺点在于如果事务涉及到多条记录操作,处理就比较困难。另外,如图中所示意,经常的工程手段会导致服务内“双写”,意思是数据库和消息队列需要同时写入。在任何涉及到多个存储的事务操作里都不可避免地涉及到分布式事务,因此很可能在服务内引入XA这样的2PC。此外还需要面临的挑战包括如何做到“幂等”操作,以及消息队列的顺序问题。如何避免双写和保证消息的顺序,我们可以从本公众号之前介绍的CDC(Change Data Capture)模式以及日志为中心的架构中得到启示。

印度最大电商Flipkart就是采用了这种手段解决跨服务的数据一致问题,并成功应用在供应链,订单,支付环节。除了消息队列之外,Flipkart引入2个设计,一个是幂等过滤器,做到每个事务操作在开始之前确保唯一性。另一个是重试队列,也就是说消息队列有2个部分组成,还有一个重试队列用于存放因服务失效而失败的事务。换句话,Flipkart不提供事务回滚。

另一种解决服务内双写问题的做法是借助于'Event Sourcing'和CQRS的设计。Event Sourcing的核心理念是只持久化状态改变的数据。例如下图的例子中,最终持久化的是下部的状态变化记录列表,持久化的数据被称为Event Store,可以根据Entity的主键检索。

大型SOA架构体系里的数据一致性问题

光有Event Sourcing还不足以解决问题,还需要借助于CQRS(Command Query Responsibility Separation)。因为Event Sourcing只提供了业务逻辑的状态变化,但缺乏最终的查询视图,而CQRS的目的就是分离查询视图和业务逻辑。

大型SOA架构体系里的数据一致性问题

组合Event Sourcing和CQRS来提供跨服务之间分布式事务的一个例子如下图所示,相关代码可以从https://github.com/cer/event-sourcing-examples来得到。

大型SOA架构体系里的数据一致性问题

无状态队列事务这种手段在Pat Helland关于分布式系统的著名必读论文'Life beyond Distributed Transactions: an Apostate's Opinion'也有类似表达。Pat Helland认为应当尽可能避免分布式事务,为此系统中引入三种抽象角色:Entity,Message,Activity。Entity表示在单个服务内事务操作的数据,Message用于在分布式事务涉及到的多个服务之间进行消息通知,而Activity则用于Entity记录状态,便于在出错时进行回滚。Google的Percolator就利用这种语义实现了分布式事务。通常,Activity可用Paxos来实现,在已有的系统里,完全实现Pat Helland的Activity抽象并不多见,像上述的Flipkart通过幂等过滤来消除Activity抽象的做法是普遍手段。

第三种做法是事务补偿。事务补偿即在事务链中的任何一个正向事务操作,都必须存在一个完全符合回滚规则的可逆事务。如果是一个完整的事务链,则必须事务链中的每一个业务服务或操作都有对应的可逆服务。有两种形式的事务补偿,一种是无状态补偿,既补偿本身作为另外的事务,通过工作流引擎提供额外业务逻辑。无状态事务补偿实际上是一种基于最终一致的反操作,因此性能相比XA要好很多,但它通常用于长期运行的服务,并且在出错时可能还需要人工干预。另一种是有状态补偿,就是说补偿操作作为事务处理的第二阶段来进行。TCC(Try-Commit-Cancel)就是这种方式的补偿。TCC是一种融合了事务补偿机制跟XA的做法。与XA协议比较,TCC位于业务服务层而非数据资源层,没有单独的准备阶段,因此TCC让服务之间的耦合更松。跟事务补偿相比,TCC消除了正向补偿的需求,但是性能和可扩展性都比较差。

大型SOA架构体系的事务处理,可以从网上找到支付宝程立在8年前的幻灯片,这也是这个话题比较全面的概括

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多