赞
踩
答案是不能,Not even close! 以原子性为例,在有多个系统的分布式系统中,一个分布式事务是在不同的系统内部执行的,我们没有办法保证它们能够同时完成,或者都不做。
jmsTransaction.begin(); // get transactions from jms session
dbTransaction.begin(); // get transactions from JDBC connection
try {
orderRepository.save(order);
jmsTemplate.convertAndSend("order:need_to_pay", dto);
dbTransaction.commit();
jmsTransaction.commit();
} catch(Exception e) {
dbTransaction.rollback();
jmsTransaction.rollback();
}
这样,如果上述代码在jmsTransaction.commit();的时候出错,这时候数据库的事务已经提交,就无法回滚。如果这时候这个方法被重新执行,数据库的操作就会被重复执行。
如果我们使用外部事务,那么这里就不会针对两个资源出现两个事务,而是只有一个事务,来统一管理多个资源。如果在多个资源上的事务出错了,外部的事务也能够保证回滚,这是通过事务的两阶段提交(2PC)来实现。使用JTA实现的事务正是这种外部事务。
由于JTA使用两阶段提交来实现多个资源之间的事务,这就会带来很大的性能问题。因为它要同步多个资源的事务,对每个资源使用两阶段提交,这就使得这个事务所花的时间比本地事务多很多。而且在这个时间段内,由于事务的隔离性,可能会造成长时间的资源占用,使得其它的事务无法同步访问该资源上的一些数据。
XA是由X/Open组织提出的分布式事务的架构(或者叫协议)。XA架构主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。也就是说,在基于XA的一个事务中,我们可以针对多个资源进行事务管理,例如一个系统访问多个数据库,或即访问数据库、又访问像消息中间件这样的资源。这样我们就能够实现在多个数据库和消息中间件直接实现全部提交、或全部取消的事务。XA规范不是java的规范,而是一种通用的规范,
JTA(Java Transaction API),是J2EE的编程接口规范,它是XA协议的JAVA实现。它主要定义了:
一个事务管理器的接口javax.transaction.TransactionManager,定义了有关事务的开始、提交、撤回等操作。
一个满足XA规范的资源定义接口javax.transaction.xa.XAResource,一种资源如果要支持JTA事务,就需要让它的资源实现该XAResource接口,并实现该接口定义的两阶段提交相关的接口。
Java 事务编程接口(JTA:Java Transaction API)和 Java 事务服务 (JTS;Java Transaction Service) 为 J2EE 平台提供了分布式事务服务。分布式事务(Distributed Transaction)包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager )。我们可以将资源管理器看做任意类型的持久化数据存储;事务管理器承担着所有事务参与单元的协调与控制。JTA 事务有效的屏蔽了底层事务资源,使应用可以以透明的方式参入到事务处理中;但是与本地事务相比,XA 协议的系统开销大,在系统开发过程中应慎重考虑是否确实需要分布式事务。若确实需要分布式事务以协调多个事务资源,则应实现和配置所支持 XA 协议的事务资源,如 JMS、JDBC 数据库连接池等。使用 JTA 处理事务的示例如下(注意:connA 和 connB 是来自不同数据库的连接)
要想使用用 JTA 事务,那么就需要有一个实现 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。XAConnection 是参与 JTA 事务的 JDBC 连接。
要使用JTA事务,必须使用XADataSource来产生数据库连接,产生的连接为一个XA连接。
XA连接(javax.sql.XAConnection)和非XA(java.sql.Connection)连接的区别在于:XA可以参与JTA的事务,而且不支持自动提交。
JTA的优点很明显,就是提供了分布式事务的解决方案,严格的ACID。
虽然JTA事务是Java提供的可用于分布式事务的一套API,但是不同的J2EE平台的实现都不一样,并且都不是很方便使用,所以,一般在项目中不太使用这种较为负责的API。现在业内比较常用的分布式事务解决方案主要有异步消息确保型、TCC、最大努力通知
public void transferAccount() {
UserTransaction userTx = null;
try{
// 获得 Transaction 管理对象
userTx = (UserTransaction)getContext().lookup("\
java:comp/UserTransaction");
// 启动事务
userTx.begin();
// 将 A 账户中的金额减少 500
stmtA.execute(sql1);
// 将 B 账户中的金额增加 500
stmtB.execute(sql2);
// 提交事务
userTx.commit();
// 事务提交:转账的两步操作同时成功(数据库 A 和数据库 B 中的数据被同时更新)
} catch(SQLException sqle){
// 发生异常,回滚在本事务中的操纵
userTx.rollback();
// 事务回滚:转账的两步操作完全撤销
//( 数据库 A 和数据库 B 中的数据更新被同时撤销)
} catch(Exception ne){
e.printStackTrace();
}
}
public void commit() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException {
// 得到当前事务中的所有事务资源
List<XAResource> list = getAllEnlistedResouces();
// 通知所有的事务资源管理器,准备提交事务
// 对于生产级别的实现,此处需要进行额外处理以处理某些资源准备过程中出现的异常
for(XAResource xa : list){
xa.prepare();
}
// 所有事务性资源,提交事务
for(XAResource xa : list){
xa.commit();
}
}
对于分布式系统来说,很难有一个类似ACID这样的标准
CAP定理,包括以下几个方面:
由于分布式系统形式的多样性和复杂性,如果想完全满足上述的原则设计一个分布式系统,几乎是不可能的。首先,分布式服系统就是要把系统的各个部分部署到不同的服务器上,那我们就必须要通过分区容错来避免由于网络、机器故障等原因造成的问题。所以分区容错性是必不可少的,否则可用性都无法保证。
对于可用性来说,如果我们要严格保证可用性,即使是在分区容错性得到保障的前提下,所有的服务都是可用的,有时候,我们也需要通过异步的方式来处理一些业务,这就会造成数据的不一致。如已经从用户账户上扣费,但是票还没有转移完成等。
再来看一致性,是否有办法能够实现呢?那我们就需要先来看看几种一致性:
在一般的分布式系统的设计中,我们大都以最终一致性为目标,来设计我们的分布式事务。这既能保证系统的可用性和容错性,也能在绝大多数情况下保证数据的弱一致性,并且在少数出错或网络高延迟的情况下,也能保证数据的最终一致性。
参考:http://codin.im/2017/05/14/rest-micro-services-distributed-trasaction-1-jta/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。