前言:
此时小伙伴们对“java 分布式事务解决框架”大约比较重视,朋友们都需要学习一些“java 分布式事务解决框架”的相关文章。那么小编在网摘上汇集了一些关于“java 分布式事务解决框架””的相关文章,希望朋友们能喜欢,姐妹们快快来学习一下吧!一、事务1 .1、什么是事务
事务可以看成是由多个小事件一起组成的一个大事件,这些小事件要么全部成功;如果有任意一个事件失败,所有事件均宣告失败,并恢复事件执行之前的样子。
事务拥有四个特征,习惯上被称为ACID特性:
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部成功执行,要么都不要执行。一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态是指数据库中的数据应满足完整性约束。隔离性(Isolation):事务的执行是相互独立的,不会相互干扰,一个事务不会看到另一个正在运行过程中事务的数据。持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。在事务结束时,操作将不可逆转。1.2、本地事务
起初,事务仅限于对单一数据库资源的访问控制:
架构服务化以后,事务的概念延伸到了服务中。倘若将一个单一的服务操作作为一个事务,那么整个服务操作只能涉及一个单一的数据库资源:
这类基于单个服务单一数据库资源访问的事务,被称为本地事务。
1.3、分布式事务
随着Internet技术的发展和数据量的不断扩大,软件系统逐渐从单一应用发展到分布式系统/微服务应用。分布式系统将单个应用系统划分为多个可以独立部署的微服务。在许多场景中,需要服务之间的远程协作来完成事务操作。在这种分布式系统环境中,事务通过网络由不同服务之间的远程协作来完成,称为分布式事务。分布式系统中的事务实际上是由多个本地事务组成的。对于分布式事务,ACID很难得到满足。
二、分布式事务应用架构
本地事务主要限制在单个会话内,不涉及多个数据库资源。但是在基于SOA(Service-Oriented Architecture,面向服务架构)的分布式应用环境下,越来越多的应用要求在同一事务中包含对多个数据库资源和服务的访问,分布式事务应运而生。
最早的分布式事务不涉及服务间的访问调用,仅仅是服务内操作涉及到对多个数据库资源的访问。
当一个服务操作访问不同的数据库资源,又希望对它们的访问具有事务特性时,就需要采用分布式事务来协调所有的事务参与者。
对于上述分布式事务应用体系结构,尽管一个服务操作可以访问多个数据库资源,但毕竟整个事务仍然控制在单个服务中。如果服务操作需要调用另一个服务,则事务需要跨多个服务。在这种情况下,当从一个服务开始的事务调用另一个服务时,它需要通过某种机制流到另一个服务,这样被调用的服务访问的资源将自动添加到事务中。下图显示了一个跨多个服务的分布式事务
如果将上述两种场景(一个服务可以调用多个数据库资源或其他服务)进行组合和扩展,整个分布式事务的参与者将形成一个树形拓扑,如下图所示。在跨服务分布式事务中,事务的发起方和提交方是相同的。它可以是整个调用客户机,也可以是客户机首先调用的服务。
基于单一数据库资源访问的本地事务,分布式事务的应用架构更为复杂。
在不同的分布式应用体系结构中,实现分布式事务需要考虑的问题并不完全相同,例如多个资源的协调、事务的跨服务传播等等。实施机制也是复杂多变的。虽然有许多工程细节需要考虑,但分布式事务的核心是ACID特性。因此,要理解分布式事务,首先应该了解它如何实现事务ACID特性。
三、解决方案3.1、两阶段提交(2PC)
我们可以从下面这个图的流程来看出中间的一些比如commit和abort的细节。
两阶段提交这种解决方案属于牺牲了一部分可用性来换取的一致性。在实现方面,在 .NET 中,可以借助 TransactionScop 提供的 API 来编程实现分布式系统中的两阶段提交,不过在多服务器之间,需要依赖于DTC来完成事务一致性,Windows下微软搞的有MSDTC服务,Linux下就不太行了。
另外补充一句,TransactionScop 默认不能用于异步方法之间事务一致,因为事务上下文是存储于当前线程中的,如果是在异步方法,需要显式地传递事务上下文。
优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(但也不能100%保证强一致)
缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景,如果分布式系统跨接口调用,目前 .NET 界还没有实现方案。
3.2、补偿事务(TCC)
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
Try 阶段主要是对业务系统做检测及资源预留Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
举个例,假如 Bob 要向 Smith 转账,思路大概是: 有一个本地方法,里面依次调用 1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱被冻结起来。 2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。 3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
优点: 跟2PC比起来,实现以及流程会简单一些,但数据的一致性比2PC要差一点
缺点: 缺点是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。
3.3、本地消息表(异步确保)
本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理,这种思路是来源于ebay。可以从下面的流程图中看出其中的一些细节:
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
3.4、MQ 事务消息
有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
第一阶段Prepared消息,会拿到消息的地址。 第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要向消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。保证了消息发送与本地事务同时成功或同时失败。
遗憾的是,RocketMQ并没有 .NET 客户端。
优点: 实现了最终一致性,不需要依赖本地数据库事务。
缺点: 实现难度大,主流MQ不支持,没有.NET客户端,RabbitMQ事务消息部分代码也未开源。
3.5、Sagas 事务模型
Saga事务模型又叫长时间运行的事务,其核心思想是「将长事务拆分为多个本地短事务」,由Saga事务协调器协调,如果正常结束那就正常完成,如果「某个步骤失败,则根据相反顺序一次调用补偿操作」。
Seata框架中一个分布式事务包含3种角色:
「Transaction Coordinator (TC)」:事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。「Transaction Manager (TM)」:控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。「Resource Manager (RM)」:控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
这里说一种基于 Sagas 机制的工作流事务模型,这个模型的相关理论目前来说还是比较新的,百度上几乎没有什么相关资料。
该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。
比如我们一次关于购买旅游套餐业务操作涉及到三个操作,他们分别是预定车辆,预定宾馆,预定机票,他们分别属于三个不同的远程接口。可能从我们程序的角度来说他们不属于一个事务,但是从业务角度来说是属于同一个事务的。
他们的执行顺序如上图所示,所以当发生失败时,会依次进行取消的补偿操作。
因为长事务被拆分了很多个业务流,所以 Sagas 事务模型最重要的一个部件就是工作流或者你也可以叫流程管理器(Process Manager),工作流引擎和Process Manager虽然不是同一个东西,但是在这里他们的职责相同。
四、总结
文章介绍了分布式事务的基础理论,并对常用的分布式事务方案进行讲解。分布式事务本身是一个技术难题,没有一种完美的方案可以应对所有场景,具体还是要根据业务场景去抉择。
标签: #java 分布式事务解决框架