• 第21章 Spring事务管理之扩展篇(三)


    第21章 Spring事务管理之扩展篇

    21.3 Spring与JTA背后的奥秘

    无论是Spring的参考文档,还是大多数介绍Spring的书籍,在提到使用Spring的JtaTransactionManager进行分布式事务管理的时候,都强调需要使用从应用服务器的JNDI服务获取的dataSource,而不是本地配置的普通dataSource(见图21-4)。

    image-20220627112922236

    那么原因是什么呢?我们知道,事务管理是要加之于具体的事务资源上的,所以,通常的PlatformTransactionManager的实现都会有相对应的事务资源的引用,比如,DataSourceTransactionManager需要指定DataSource,HibernateTransactionManager需要指定SessionFactory等。

    下方代码清单再次演示了使用DataSourceTransactionManager的情况。

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPoo1edDataSource" destroy-method="close">
    	<property name="driverClass" value="$(jdbc.driverClassName)"/>
    	<property name="jdbcUrl" value="$(jdbc.url)"/>
    	<property name="user" value="$(jdbc.username)"/>
    	<property name="password" value="$íjdbc.password)"/>
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource"/>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从常识上来讲,像DataSourceTransactionManager这样才是正常的情况。可是,我们也看到,JtaTransactionManager确实没有明确指定依赖于哪个资源,却依然能够将需要加入分布式事务的资源纳入其管理范围。它是怎么做到的呢?如果揭开这个谜团,是否就能搞清楚在使用JtaTransactionManager的时候,要求我们使用从应用服务器的JNDI服务查找到的DataSource的真正原因呢?

    在介绍JtaTransactionManager的时候,我们说到,JtaTransactionManager只是对各种具体的JTA实现产品提供的分布式事务管理功能进行了封装,JtaTransactionManager会将最终的工作委派给具体的JTA实现来做。 所以,追根溯源,我们不得不追到JTA规范以及JTA实现中。

    略去JTA以及X/Open规范千言不提,我们还是长话短说,直接进入正题吧!

    首先,具体的事务资源,RDBMS、MessageQueue等,要加入JTA管理的分布式事务,JTA规范要求其实现javax.transaction.xa.XAResource接口。所以,希望加入JTA管理的分布式事务的RM(ResourceManager,资源管理器)通常会提供相应的适配器(Adaptor),用于提供基于XAResource的分布式事务交互能力,比如关系数据库提供的支持XA的JDBC驱动程序,就是这样的适配器。这样,所有资源管理与事务交互的工作,基本上就由RM的适配器来统一管理了。

    在想要参与JTA分布式事务的事务资源拥有了XAResource支持之后,JTA的javax.transaction.TransactionManager(我们称其为JTATransactionManager,区别于Spring的JtaTransactionManager)与RM之间就可以进行通信,如图21-5所示。

    image-20220627114711725

    适配器通常都有应答能力,这样,在JTATransactionManager使用两阶段提交协议管理分布式事务的过程中,可以同每个RM进行交互。

    不过,JTATransactionManager在事务管理过程中要与哪些RM打交道,却不是由它自己说了算的。

    想要参与JTA分布式事务的RM何时何地甚至怎样加入JTATransactionManager管理的分布式事务,也不是每个RM自己说了算。

    JTATransactionManager与各个RM之间的联系要由ApplicationServer(一般意义上的TPMonitor)来进行协调! ApplicationServer为基于JTA的分布式事务提供运行时环境,并负责协调JTATransactionManager与各RM之间的交互,整个过程类似于如下所叙述的。

    (1)ApplicationServer一开始当然要先通过JNDI绑定它的JTA实现中的UserTransaction或者TransactionManager具体实现类,这样,客户端应用程序就可以通过JNDI获取它们。现在客户端应用程序想要开始一个分布式事务,进而UserTransaction或者TransactionManager的相应方法被调用。

    (2)ApplicationServer内部会要求TransactionManager为当前事务分配一个唯一的标志(以Xid表示),然后开始事务,并将当前事务绑定到当前线程。

    (3)客户端跟ApplicationServer要求相应的数据资源进行数据访问,ApplicationServer会跟RM的适配器要一个事务资源对象,我们暂且称之为TransactionalResource。该资源对象包含两部分,一部分是JTATransactionManager需要与之交互的XAResource,另一部分是要公开给客户端应用程序使用的Connection资源,取得TransactionalResource之后,ApplicationServer要做如下两件事情。

    a)ApplicationServer从TransactionalResource中取得XAResource交给TransactionManager。TransactionManager开始通过获得的这个XAResource与RM进行交互。实际上,现在TransactionManager只是调用xAResource的start(xid)方法通知RM开始记录。

    b)ApplicationServer然后再把与XAResource属于同一个TransactionalResource的Connection传给客户端应用程序使用,然后客户端应用程序就可以使用ApplicationServer传给的Connection进行数据访问操作了。

    (4)客户端应用程序数据访问操作完成,关闭之前ApplicationServer传给的Connection,ApplicationServer在感知到Connection被关闭之后,会通知lTransactionManager,TransactionManager则调用与这个Connection属于同一个TransactionalResource的XAResource的end(xid)方法结束事务记录。如果在当前分布式事务期间还有使用其他RM进行的数据操作,ApplicationServer以几乎同样的方式从RM的适配器那里获取TransactionalResource类似的对象,然后协调TransactionManager重复余下的工作。

    (5)当客户端通过UserTransaction或者TransactionManager的相应方法要求结束事务的时候,ApplicationServer就会通知TransactionManager使用两阶段提交协议提交当前事务:

    a)TransactionManager调用xAResource的prepare(xid)方法通知各个RM准备提交事务;

    b)如果各个XAResource回答全部OK,TransactionManager调用XAResource的commit(xid)方法通知各个RM最终提交事务。

    可见,各个RM确实参与到了JTATransactionManager所管理的分布式事务中,只不过,参与的过程由ApplicationServer对客户端应用程序屏蔽了。之所以要求客户端应用程序通过应用服务器的JNDI获取DataSource等资源,是因为只有使用ApplicationServer公开的与XAResource绑定到同一TransactionalResource的Connection,才可以保证客户端应用程序所做的所有数据访问操作能够加入ApplicationServer所协调的分布式事务中(如图21-6所示)。

    image-20220627120129304

    ApplicationServer为客户端应用程序公开与当前分布式事务相关的Connection的方式,就是实现一个DataSource,然后把该DataSource绑定到JNDI。这样,客户端应用程序就可以通过从JNDI取得的DataSource中获取与事务相关的Connection了。 如果使用本地定义的DataSource,因为它与当前分布式事务不发生任何关系,所以,也就根本不可能参与到分布式事务中去。

    不过,如果我们使用的JTA实现不是相应的ApplicationServer提供的,比如,可以独立使用的Atomikos或者JOTM等JTA实现,要求我们从应用服务器的JNDI服务取得相应的DataSource这一前提也是不成立的。这时,我们直接使用各个JTA产品提供的DataSource封装类进行数据访问即可,与ApplicationServer屏蔽掉RM与TransactionManager之间的关系一样,这些产品也有与ApplicationServer完成同样工作的角色,为我们关联具体的RM与当前产品的TransactionManager。

    下方代码清单中是在Spring中使用Atomikos的典型配置方式。

    <bean id="datasource1" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
    	<property name="uniqueResourceName" value="XADBMS_ONE"/>
    	<property name="xaDataSourceClassName" value="COM.FirstSQL.Dbcp.DbcpXADataSource"/>
    	<property name="xaDataSourceProperties" va1ue="user=username;portNumber=8000"/>
    	<property name="exclusiveConnectionMode" value="true"/>
    </bean>
    
    <bean id="datasource2" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
    	<property name="uniqueResourceName" value="XADBMS_TWO"/>
    	<property name="xaDataSourceClassName" value="COM.FirstSQL.Dbep.DbcpXADataSource"/>
    	<property name="xaDataSourceProperties" value="user=username;portNumber=8000"/>
    	<property name="exclusiveConnectionMode" value="true"/>
    </bean>
    
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
    	<property name="forceShutdown" value="true"/>
    </bean>
    
    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
    	<property name="transactionTimeout" value="200"/>
    </bean>
    
    <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    	<property name="transactionManager" ref="atomikosTransactionManager"/>
    	<property name="userTransaction" ref="atomikosUserTransaction"/>
    </bean>
    
    <bean id="dao" class="...">
    	<property name="dataSource" ref="datasource"/>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    我们需要告知Spring的JtaTransactionManager使用Atomikos的usertransaction和ttransactionManager实现。而至于Atomikos的userTransaction和transactionManager到底如何与RM(数据库、消息队列以及其他)进行交互,那就是Atomikos的事情了。

    注意:不要被Atomikos的SimpleDataSourceBean的名字给迷惑了。另外,只要应用程序使用的数据库提供了有XA支持的数据库驱动,我们就可以通过SimpleDataSourceBean来配置需要的支持XA的DataSource,但各个参数的设置需要参考相应驱动程序的文档,比如xaDataSourceClassName以及xaDataSourceProperties属性。

    多个Atomikos的SimpleDataSourceBean存在的情况下,它们对应的uniqueResourceName必须是不同的!

    最后的内容是专门为Spring的JtaTransactionManager的,Spring2.5版本发布后,在XSD的配置中,可使用tx命名空间下专门为JtaTransactionManager提供的配置元素来简化其配置,如下所示:

    <tx:jta-transaction-manager/>
    
    • 1

    21.4 小结

    作为Spring事务抽象框架的扩展篇,本章主要挖掘了事务抽象框架中有关ThreadLocal的实践、策略模式的实践,以及分布式事务管理的简单分析这三点内容。我希望以这些内容为砖,能够引出更多的玉。

  • 相关阅读:
    合并两个升序链表,哨兵位的理解
    常用的软件配置管理工具
    OpenMPI的安装与运行分布式项目
    基于Echarts实现可视化数据大屏董事会指标体系层级结构系统
    打码平台之图鉴的使用步骤
    C++战火英雄
    webpack5基础--13_生产模式介绍
    【大数据分析专业之基于python的企业数据大屏可视化分析系统-哔哩哔哩】 https://b23.tv/hUraRJV
    Jetpack架构组件学习(4)——APP Startup库的使用
    免费SSL证书与付费SSL证书的区别
  • 原文地址:https://blog.csdn.net/qq_34626094/article/details/125499339