如何在Spring和GlassFish 5中进行分布式事务XA?
我正在尝试创建一个包含两个REST Web服务的事务,其数据源指向相同的数据库。 第一个名为1
服务使用Spring RestTemplate调用另一个名为2
Web服务。
为了实现事务,我使用的是JNDI连接池,MySql JDBC驱动程序(版本5.1.35),JTA,XA,Spring和GlassFish 5 AppServer。
现在,我已经在Spring项目中下载了maven依赖项,使用JtaTransactionManager
定义了一个配置类,并在application.yml
文件中配置了数据源和JTA属性,如下面的代码所示:
配置类:
@Configuration @EnableTransactionManagement public class Transacciones { @Bean public PlatformTransactionManager platformTransactionManager(){ return new JtaTransactionManager(); } }
application.yml文件
spring: datasource: jndi-name: jdbc/Prueba driver-class-name: com.mysql.jdbc.Driver jta: enabled: true
我在GlassFish 5中配置了JNDI数据源,使用名为pruebaXA
的javax.sql.XADataSource
数据源在“Connections pools”页面中定义名为jdbc/Prueba
的“JDBC资源”:
在Web服务1
的控制层中,该方法使用Spring Framework的RestTemplate
类调用服务2
:
服务1代码:
@RestController @RequestMapping("/servicio") @EnableTransactionManagement public class a { @Autowired private JdbcTemplate objJdbcTemplate; @Transactional(rollbackFor = RuntimeException.class) @GetMapping("/1") public Integer getValor(){ try{ int numero; int n=50; RestTemplate restTemplate = new RestTemplate(); Integer intRes1; Integer intRes2; numero = (int) (Math.random() * n) + 1; intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0-SNAPSHOT/servicio/2",numero,Integer.class); intRes1=objJdbcTemplate.update("INSERT INTO A VALUES(" +numero + ")"); return numero; }catch(Exception e){ throw new RuntimeException(e); } } }
服务2代码:
@RestController @RequestMapping("/servicio") public class a { @Autowired private JdbcTemplate objJdbcTemplate; @Transactional(rollbackFor = RuntimeException.class) @PostMapping("/2") public Integer getValor(@RequestBody Integer intNum){ try{ Integer intRes; intRes=objJdbcTemplate.update("INSERT INTO B VALUES(" + intNum + ")"); return intRes; }catch(Exception e){ throw new RuntimeException(e); } } }
如果两个服务没有错误,则没有问题。 但是,当服务1
下降时,服务2
不知道错误并且不进行回滚。
我不知道是否需要在GlassFish 5或Spring程序中配置另一个function/选项。
我已经读过,在Spring中只需要一个JtaTransactionManager
bean,框架执行与配置和使用JTA事务相关的所有工作。 spring和JTA
JTA
现在,如果您仍然需要使用JTA,至少可以选择您的选择。 有两种常见的情况:在重量级应用程序服务器中使用JTA(具有绑定到JavaEE服务器的所有令人讨厌的缺点),或使用独立的JTA实现。 Spring通过名为JtaTransactionManager的PlatformTransactionManager实现为基于JTA的全局事务实现提供支持。 如果在JavaEE应用程序服务器上使用它,它将自动从JNDI中找到正确的javax.transaction.UserTransaction引用。 此外,它将尝试在9个不同的应用程序服务器中查找特定于容器的javax.transaction.TransactionManager引用,以用于更高级的用例,例如事务暂停。 在幕后,Spring加载了不同的JtaTransactionManager子类,以便在可用时利用不同服务器中的特定额外function,例如:WebLogicJtaTransactionManager,WebSphereUowTransactionManager和OC4JJtaTransactionManager。
因此,如果您在Java EE应用程序服务器内部并且无法转义,但想要使用Spring的JTA支持,那么您可以使用以下命名空间配置支持来正确(并自动)生成JtaTransactionManager:
或者,您可以根据需要注册JtaTransactionManager bean实例,不带构造函数参数,如下所示:
@Bean public PlatformTransactionManager platformTransactionManager(){
return new JtaTransactionManager(); } Either way, the end result in a JavaEE application server is that you can now use JTA to manage
感谢Spring,你的交易是统一的。
谢谢你的帮助和时间。
如何进行分布式事务XA?
如果首先调用REST或Web服务,然后再调用另一个,则两个操作都不会成为事务的一部分。 要形成交易,这些操作必须“开始”交易或“加入”现有交易。 要执行该事务,您的程序必须与事务监视器(TM)交互,例如AT&T / Oracle Tuxedo (80年代发布), X / Open XA标准 (90年代发布)和基于JTA的系统。 。
请注意基于TM的事务如何工作:
-
使用XA数据源的事务基本上是一个在两个不同数据库上调用数据库操作的程序。 相同的程序(例如,调用一个或多个bean中的方法)启动事务,对数据库执行某些操作并对另一个数据库执行其他操作。 如果其中一个操作失败,则不执行其他操作或回滚操作。
-
使用XA数据源和支持JTA的组件的事务基本上是一个程序,它将一个或多个数据库上的操作与其他启用事务的操作相结合。 例如,您可以将数据库上的操作与内容存储库或基于网络的文件系统上的操作相结合。 您可以定义在数据库操作失败时不对文件系统执行或回滚操作的事务。 通过在事务监视器中定义操作和补偿,可以将非事务性应用程序(如基于COBOL的程序)集成到事务中。
-
集成Web服务的事务需要特殊处理。 例如,有一个Web服务事务的提议,例如WS-Transaction和WS-Coordination规范。 有一个协调器 ,它像事务监视器一样工作。 软件必须调用协调器才能启动事务。 事务中的每个参与者都会报告每个操作是成功还是失败。 协调器根据结果调用其他操作或调用补偿。
如今,有一些软件架构的提议不依赖于基于TM的事务。 基于CQRS和Event Sourcing的设计使用Sagas设计模式实现事务。 如果您对定义调用两个REST服务的类事务操作感兴趣,可以考虑避免使用XA / JTA并编写Sagas。
如何在Spring和GlassFish 5中进行分布式事务XA?
您可以在Internet上查看许多教程。 例如,
- 一个向您展示三个用例的教程 :一个更新两个数据库,一个组合数据库操作和传出JMS消息,另一个组合传入JMS消息和数据库操作。
- 一个video,描述了如何使用JTA管理Spring中的分布式事务
- 以及Spring Framework的文档 。
如果两个服务没有错误,则没有问题。 但是,当服务
1
下降时,服务2
不知道错误并且不进行回滚。
这是正确的行为。 调用REST / Web服务时,该REST / Web服务执行的操作不会加入事务。 如果调用服务失败,则调用的服务将不会注意到它,也不会回滚它们在数据库中的操作。
我不知道是否需要在GlassFish 5或Spring程序中配置其他function/选项。
不需要。您只需配置XA数据源。 Spring将使用您的配置类和注释将应用程序在这些数据源上执行的操作自动连接到事务中。 例如,如果您有一个bean调用多个方法来对一个或多个XA数据源执行操作,那么这些操作将加入到一个事务中。
但是,当您在另一个应用程序中调用REST / webservice中的方法时,该REST / Web服务执行的数据库操作将不会加入该事务。
其余的Web服务(基于http)本质上是非事务性的(它们是基于http的)。 您已经使每个方法/操作都是事务性的,但它们不共享资源之间的任何状态(rest操作)。 通常 – 您可以通过数据库或消息传递进行XA事务,而不是通过http调用。
intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0- SNAPSHOT/servicio/2",numero,Integer.class);
调用远程Web服务没有任何事务上下文。 如果需要维护服务之间的事务,请将secord服务作为EJB(或作为注入的托管bean)调用
基本上:使用基于http的rest服务 – 忘记它们之间的任何交易。 协议(HTTP)不是为此而构建的。
我唯一看到事务性的是带有WS-RM扩展的SOAP(带有可靠消息传递的SOAP)。但是它设置起来不是很容易(阅读:当你不知道什么是它时,它可能是噩梦你这么做)并不是所有的WS框架都支持它。
当你真的需要在Web服务之间提供可靠的交付时,有一种方法。 通常用于实现有保证的交付的是具有存储转发模式的幂等服务( https://en.m.wikipedia.org/wiki/Idempotence )的消息传递。 简单来说 – 服务1将JMS消息存储到队列中,并且存在调用服务2的处理器(MDB)。(是的,调用远程Web服务可能会发生服务2将多次接收消息。不约束是一个如何处理它。)