1、分布式事务模型 ACID 实现
1.1、X/Open XA 协议(XA)
最早的分布式事务模型是 X/Open 国际联盟提出的 X/Open Distributed Transaction Processing(DTP)模型,也就是大家常说的 X/Open XA 协议,简称XA 协议。 DTP模型如图:
- TM:全局事务管理器
- RM:多个资源管理器
- AP:应用程序
全局事务管理器负责管理全局事务状态与参与的资源,协同资源一起提交或回滚;资源管理器则负责具体的资源操作。 XA 协议主要描述了 TM 与 RM 之间的接口,允许多个资源在同一分布式事务中访问。 基于 DTP 模型的分布式事务流程大致如下:
- XA接口详解XA接口时双向的系统接口,用于事务管理器和多个资源管理器之间形成桥梁。
- 事务管理器控制JTA事务,管理事务生命周期,并协调资源,在JTA中,事务管理器抽象为TransactionManager接口,并通过底层事务服务JTS(java事务服务)实现,资源管理器负责控制和管理实际资源(如数据库或者JMS(java消息服务)队列)。 下图说明事务管理器、资源管理器,以及JTA环境中客户应用之间的关系:
1.2、阶段提交(2PC)
(1)概念:二阶段提交(Two-phaseCommit)是指,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm),通常二阶段提交也被称为是一种协议 。(2)二阶段算法思路: 参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。(3)协议假设
- 在分布式系统中,存在一个节点作为协调者,则其他节点作为参与者,且各个节点之间可以进行网络通讯。
- 所有节点都采用预写式日志,切日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失。
- 所有节点不会永久损失,即使损坏后任然可以恢复。 参与者与协调者之间的关系如图:
- (4)第一阶段(提交请求阶段)
- 协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
- 参与者节点执行询问发起为止的所有事务操作,并将信息写入日志。
- 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。
(5)第二阶段(提交执行阶段)
- 成功:
当协调者节点从所有参与者节点获得的相应消息都为"同意"时:
- 协调者节点向所有参与者节点发出"正式提交"的请求。
- 参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
- 参与者节点向协调者节点发送"完成"消息。
- 协调者节点收到所有参与者节点反馈的"完成"消息后,完成事务。
- 失败
如果任一参与者节点在第一阶段返回的响应消息为"终止",或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:
- 协调者节点向所有参与者节点发出"回滚操作"的请求。
- 参与者节点利用之前写入的日志信息执行回滚,并释放在整个事务期间内占用的资源。
- 参与者节点向协调者节点发送"回滚完成"消息。
- 协调者节点收到所有参与者节点反馈的"回滚完成"消息后,取消事务。
(6)用事务来解释二阶段所谓的二阶段时将提交过程分为两个阶段:
- 1.准备阶段
- 2.提交阶段
(7)JTA(Java Transaction API)与atomikos实现分布式事务主要代码如下:
- 1> 配置数据源: DruidXADataSource 连接池实现了XAResource接口用来进行对资源操作
@Bean(name = "systemDataSource")@Primarypublic DataSource systemDataSource(){ AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); ds.setXaProperties(PojoUtil.obj2Properties(postgreSqlProperties)); ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); ds.setUniqueResourceName("systemDataSource"); ds.setPoolSize(5); ds.setTestQuery("SELECT 1"); return ds;}复制代码
- 2> 配置事务: 配置spring的JtaTransactionManager,其底层交给atomikos进行事务处理。
@Bean(name = "transactionManager")@DependsOn({"userTransaction