现如今人人都在考虑和构建微服务,当然我也是。微服务本质上来说就是一个分布式系统。
什么是分布式事务?
事务通过网络连接跨多个物理系统或计算机,简称为分布式事务。在微服务当中一个事务会分布到多个微服务中,通过有序地调用完成一个完整的事务。
下面是一个使用事务的单体电子商务系统:
在以上系统中,如果用户向平台发送结算请求,平台会创建一个跨多个表的本地事务,来处理订单以及从仓储中预留物品。任何步骤失败,事务会回滚,包括订单和预留物品都回滚。这就是所谓的ACID(原子性、一致性、隔离性、持久性),这些是通过数据库系统来保证的。
下面是拆分成微服务的电子商务系统:
当系统被拆分后,创建了两个新的微服务:OrderMicroservice和InventoryMicroservice,这两个微服务有独立的数据库。当用户发起结算请求,这两个微服务都会发起数据库修改。因为事务跨多个数据库系统,现在事务会当成分布式事务来处理。
微服务中分布式事务会有什么问题?
随着微服务架构的出现,我们正在失去数据库的ACID特性。事务现在可以跨越多个微服务,因此可以跨越多个数据库。我们将面临的关键问题是:
如何保持事务的原子性?
事务的原子性指的是要么所有步骤都成功,要么都失败。在以上示例中,如果InventoryMicroservice微服务中预留物品失败,如何让OderMicroservice中的订单处理事务回滚。
如何处理当前请求?
如果其中的一个微服务中的一个对象已经持久化到数据库,同时另一个请求读取了同一个对象。服务该回退到旧数据还是新的?在上面的例子中,一旦OrderMicroservice完成了,InventoryMicroservice现在执行更新,用户请求订单数是否包含当前这个订单?
如今的系统是为故障而设计的,面临的一些主要问题就是处理分布式事务。
解决方案
以上两个问题在设计和创建微服务架构是非常重要的。以下列出了一些解决方法:
- 两阶段提交
- 最终一致性和补偿/SAGA
1、两阶段提交
顾名思义,这种处理事务的方法包含两个阶段,准备阶段和提交阶段。其中事务协调器是重要的参与组件,维护事务的生命周期。
工作原理:
在准备阶段,所有参与的微服务准备提交并通知协调器它们已经准备好提交事务。在提交阶段,事务协调器向所有微服务发出提交或回滚命令。
以电子商务系统为例:
在上面的示例中,当用户发起结算请求,协调器会启动一个包含所有上下文信息的全局事务。首先向OrderMicroservice发送一个准备命令,创建一个订单。然后会向InventoryMicroservice发送一个预留物品的准备命令。当两个微服务都返回准备充分,它们锁定对象,防止被更新并通知协调器。一旦协调器确认所有微服务已经准备更新就绪,它将通过请求事务提交来要求他们持久化自己的修改。此时,所有对象都将被解锁。
在失败情况下,如果任意微服务准备失败,协调器会终止事务并执行回滚流程。在上图中,订单微服务因为某种原因创建订单失败,但inventoryMicroservice已经回复准备就绪。协调器会向inventoryMicroservice发起终止事务,仓库微服务会回滚任何更新操作并对锁定对象进行解锁。
优势
- 该方法保证事务是原子的。事务结束时,要么所有微服务都成功,要么所有微服务都没有变化。
- 其次,它允许读写隔离,在事务协调器提交更改之前,对象上的更改是不可见的。
- 该方法是一个同步调用,其中客户端将收到成功或失败的通知。
劣势
- 与单个微服务的操作时间相比,两阶段提交相当慢。它们高度依赖于事务协调器,在高负载期间,事务协调器确实会降低系统的运行速度。
- 另一个主要缺点是数据库行的锁定。锁可能成为性能瓶颈,并且可能出现死锁,即两个事务相互锁住对方。
2、最终一致性和补偿/SAGA
最终一致性的最佳定义参考文章:当需要更新数据时,每个服务都会发布一个事件。其他服务订阅事件。当接收到事件时,服务更新其数据。
在这种方法中,分布式事务由相关服务通过本地异步事务来完成。微服务通过事件总线来互相通信。
工作原理:
还是 以电子商务系统为例:
在以上示例中,客户端向系统请求处理订单。在请求当中Choreographer(编排器)发布一个创建订单的事件,表示开启一个事务。OrderMicroservice监听这个事件并创建订单,如果成功就发布订单创建事件。Choreographer(编排器)监听订单创建成功事件并发布预留物品事件。InventoryMicroservice监听预留物品事件,并处理预留物品事务,如果成功就发布物品预留成功事件。到这里表示一个完整事务结束。
微服务之间所有基于事件的通信都是通过事件总线进行的,并由编排器来处理业务复杂问题。
如果InventoryMicroservice因某种原因更新失败,会发送预留物品失败事件。编排器监听到该事件后,通过发布删除订单事件,启动补偿事务。OrderMicroservice监听到删除事件后,删除已经创建的订单。
优势
这种方法的一大优点是每个微服务只关注它自己的原子事务。如果另一个服务占用的时间较长,Microservice不会被阻塞。这也意味着不需要数据库锁。由于其基于异步事件的解决方案,使用这种方法可以使系统在高负载下具有高度的可伸缩性。
劣势
这种方法的主要缺点是没有读取隔离。这意味着,在上面的示例中,客户端可以看到订单已经创建,但是在下一秒,由于补偿事务,订单被删除了。而且,当微服务的数量增加时,调试和维护就会变得更加困难。
总结
首选是避免使用分布式事务。如果是一个正在构建的新应用程序,那么从Martin Fowler在MonolithFirst中描述的单体开始。引用一段话:
一种更常见的方法是从单体开始,逐渐剥离边缘的微服务。这种方法可能会在微服务架构的核心留下一个巨大的整体,但大多数新开发都发生在微服务中,而整体则相对静止。-马丁