百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

JTA/XA全局事务解决方案atomikos 香港入境事务处官网

lipiwang 2024-10-22 15:51 5 浏览 0 评论

Atomikos

Atomikos公司官方网址为:https://www.atomikos.com/。其旗下最著名的产品就是事务管理器。产品分两个版本:

  • TransactionEssentials:开源的免费产品
  • ExtremeTransactions:上商业版,需要收费。

这两个产品的关系如下图所示:

TransactionEssentials:

1、实现了JTA/XA规范中的事务管理器(Transaction Manager)应该实现的相关接口,如:

UserTransaction实现是com.atomikos.icatch.jta.UserTransactionImp,用户只需要直接操作这个类

TransactionManager实现是com.atomikos.icatch.jta.UserTransactionManager

Transaction实现是com.atomikos.icatch.jta.TransactionImp

2、针对实现了JDBC规范中规定的实现了XADataSource接口的数据库连接池,以及实现了JMS规范的MQ客户端提供一层封装。

在上一节我们讲解JTA规范时,可以看之前文章。提到过XADataSource、XAConnection等接口应该由资源管理器RM来实现,而Atomikos的作用是一个事务管理器(TM),并不需要提供对应的实现。而Atomikos对XADataSource进行封装,只是为了方便与事务管理器整合。封装XADataSource的实现类为AtomikosDataSourceBean。典型的XADataSource实现包括:

1、mysql官方提供的com.mysql.jdbc.jdbc2.optional.MysqlXADataSource

2、阿里巴巴开源的druid连接池,对应的实现类为com.alibaba.druid.pool.xa.DruidXADataSource

3、tomcat-jdbc连接池提供的org.apache.tomcat.jdbc.pool.XADataSource

而其他一些常用的数据库连接池,如dbcp、dbcp2或者c3p0,目前貌似尚未提供XADataSource接口的实现。如果提供给AtomikosDataSourceBean一个没有实现XADataSource接口的数据源,如c3p0的ComboPooledDataSource,则会抛出类似以下异常:

ExtremeTransactions在TransactionEssentials的基础上额外提供了以下功能:

支持TCC:这是一种柔性事务

支持通过RMI、IIOP、SOAP这些远程过程调用技术,进行事务传播。

直接使用TransactionEssentials的API

新建一个maven项目,并添加如下依赖:

<dependency>
   <groupId>com.atomikos</groupId>
   <artifactId>transactions-jdbc</artifactId>
<version>4.0.6</version>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.39</version>
</dependency>
<dependency>
     <groupId>javax.transaction</groupId>
     <artifactId>jta</artifactId>
    <version>1.1</version>
 </dependency>

新建mysql数据库表

需要注意的是,在mysql中,只有innodb引擎才支持XA事务,所以这里显式的指定了数据库引擎为innodb。

    -- 新建数据库db_user;
    create database db_user;
    -- 在db_user库中新建user表
    create table db_user.user(id int AUTO_INCREMENT PRIMARY KEY,name varchar(50)) engine=innodb;
    -- 新建数据库db_account;
    create database db_account;
    -- 在db_account库中新建account表
    create table db_account.account(user_id int,money double) engine=innodb;

db_user库和db_account库是位于同一个mysql实例中的。

代码示例

在使用了事务管理器之后,我们通过atomikos提供的UserTransaction接口的实现类com.atomikos.icatch.jta.UserTransactionImp来开启、提交和回滚事务。而不再是使用java.sql.Connection中的setAutoCommit(false)的方式来开启事务。其他JTA规范中定义的接口,开发人员并不需要直接使用。

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.jdbc.AtomikosDataSourceBean;

import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

public class AtomikosExample {

	private static AtomikosDataSourceBean createAtomikosDataSourceBean(String dbName) {
		// 连接池基本属性
		Properties p = new Properties();
		p.setProperty("url", "jdbc:mysql://localhost:3306/" + dbName);
		p.setProperty("user", "root");
		p.setProperty("password", "");

		// 使用AtomikosDataSourceBean封装com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
		AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
		//atomikos要求为每个AtomikosDataSourceBean名称,为了方便记忆,这里设置为和dbName相同
		ds.setUniqueResourceName(dbName);
		ds.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
		ds.setXaProperties(p);
		return ds;
	}

	public static void main(String[] args) {

		AtomikosDataSourceBean ds1 = createAtomikosDataSourceBean("db_user");
		AtomikosDataSourceBean ds2 = createAtomikosDataSourceBean("db_account");

		Connection conn1 = null;
		Connection conn2 = null;
		PreparedStatement ps1 = null;
		PreparedStatement ps2 = null;

		UserTransaction userTransaction = new UserTransactionImp();
		try {
			// 开启事务
			userTransaction.begin();

			// 执行db1上的sql
			conn1 = ds1.getConnection();
			ps1 = conn1.prepareStatement("INSERT into user(name) VALUES (?)", Statement.RETURN_GENERATED_KEYS);
			ps1.setString(1, "tianshouzhi");
			ps1.executeUpdate();
			ResultSet generatedKeys = ps1.getGeneratedKeys();
			int userId = -1;
			while (generatedKeys.next()) {
				userId = generatedKeys.getInt(1);// 获得自动生成的userId
			}

			// 模拟异常 ,直接进入catch代码块,2个都不会提交
//        int i=1/0;

			// 执行db2上的sql
			conn2 = ds2.getConnection();
			ps2 = conn2.prepareStatement("INSERT into account(user_id,money) VALUES (?,?)");
			ps2.setInt(1, userId);
			ps2.setDouble(2, 10000000);
			ps2.executeUpdate();

			// 两阶段提交
			userTransaction.commit();
		} catch (Exception e) {
			try {
				e.printStackTrace();
				userTransaction.rollback();
			} catch (SystemException e1) {
				e1.printStackTrace();
			}
		} finally {
			try {
				ps1.close();
				ps2.close();
				conn1.close();
				conn2.close();
				ds1.close();
				ds2.close();
			} catch (Exception ignore) {
			}
		}
	}
}

运行上述的代码,数据库表插入相应的数据。将代码中间异常注释去掉,模拟异常,再运行一遍,发现两个库都没有插入数据。

TransactionEssentials与spring、mybatis整合

在pom中添加以下依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.3.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>

新建User实体

public class User {
       private int id;
       private String name;
       // setters and getters
    }

新建Account实例

public class Account {
       private int userId;
       private double money;
       // setters and getters
    }

新建UserMapper接口,为了方便,这里使用了mybatis 注解方式,没有编写映射文件,作用是一样的

public interface UserMapper {
  
   @Insert("INSERT INTO user(id,name) VALUES(#{id},#{name})")
   @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
   public void insert(User user);
  
}

新建AccountMapper接口

public interface AccountMapper {   
  
  @Insert("INSERT INTO account(user_id,money) VALUES(#{userId},#{money})")    
		public void insert(Account account);
  
}

新建使用JTA事务的bean,注意在使用jta事务的时候,依然可以使用spring的声明式事务管理

public class JTAService {

	@Autowired
	private UserMapper userMapper;//操作db_user库
	
	@Autowired
	private AccountMapper accountMapper;//操作db_account库
	
	@Transactional
	public void insert() {
		User user = new User();
		user.setName("wangxiaoxiao");
		userMapper.insert(user);

		//    int i = 1 / 0;//模拟异常,spring回滚后,db_user库中user表中也不会插入记录
		Account account = new Account();
		account.setUserId(user.getId());
		account.setMoney(123456789);
		accountMapper.insert(account);
	}
	
}

编写配置文件spring-atomikos.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--==========针对两个库,各配置一个AtomikosDataSourceBean,底层都使用MysqlXADataSource=====================-->
    <!--配置数据源db_user-->
    <bean id="db_user" class="com.atomikos.jdbc.AtomikosDataSourceBean"
          init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="ds1" />
        <property name="xaDataSourceClassName"
                  value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="url">jdbc:mysql://localhost:3306/db_user</prop>
                <prop key="user">root</prop>
                <prop key="password"></prop>
            </props>
        </property>
    </bean>

    <!--配置数据源db_account-->
    <bean id="db_account" class="com.atomikos.jdbc.AtomikosDataSourceBean"
          init-method="init" destroy-method="close">
        <property name="uniqueResourceName" value="ds2" />
        <property name="xaDataSourceClassName"
                  value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="url">jdbc:mysql://localhost:3306/db_account</prop>
                <prop key="user">root</prop>
                <prop key="password"></prop>
            </props>
        </property>
    </bean>

    <!--=============针对两个数据源,各配置一个SqlSessionFactoryBean============ -->
    <bean id="ssf_user" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="db_user" />
    </bean>

    <bean id="ssf_account" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="db_account" />
    </bean>

    <!--=============针对两个SqlSessionFactoryBean,各配置一个MapperScannerConfigurer============ -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="ssf_user"/>
        <!--指定com.test.atom.mapper.ds_user包下的UserMapper接口使用ssf_user获取底层数据库连接-->
        <property name="basePackage" value="com.test.atom.mapper.ds_user"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="ssf_account"/>
        <!--指定com.test.atom.mapper.ds_account包下的AccountMapper接口使用ssf_account获取底层数据库连接-->
        <property name="basePackage" value="com.test.atom.mapper.ds_account"/>
    </bean>

    <!--================配置atomikos事务管理器========================-->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"
          destroy-method="close">
        <property name="forceShutdown" value="false"/>
    </bean>

    <!--============配置spring的JtaTransactionManager,底层委派给atomikos进行处理===============-->
    <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="atomikosTransactionManager"/>
    </bean>

    <!--配置spring声明式事务管理器-->
    <tx:annotation-driven transaction-manager="jtaTransactionManager"/>

    <bean id="jtaService" class="com.test.atom.service.JTAService"/>
</beans>

测试代码

public class AtomikosSpringMybatisExample {
	
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-atomikos.xml");
		JTAService jtaService = context.getBean("jtaService", JTAService.class);
		jtaService.insert();
	}
}

运行mian方法,代码执行后,db_user库的user表和db_account的account表中的确各插入了一条记录。

然后将JTAService中的异常模拟的注释打开,会发现出现异常后,两个库中都没有新插入的数据库,说明我们使用的JTA事务管理器的确保证数据的一致性了。

Atomikos配置

在掌握了Atomikos基本使用之后,我们对Atomikos的配置进行一下简单的介绍。Atomikos在启动后,默认会从以下几个位置读取配置文件,这里笔者直接贴出atomikos源码进行说明:

com.atomikos.icatch.provider.imp.AssemblerImp#initializeProperties方法中定义了配置加载顺序逻辑:

    @Override
        public ConfigProperties initializeProperties() {
            //读取classpath下的默认配置transactions-defaults.properties
            Properties defaults = new Properties();
            loadPropertiesFromClasspath(defaults, DEFAULT_PROPERTIES_FILE_NAME);
            //读取classpath下,transactions.properties配置,覆盖transactions-defaults.properties中相同key的值
            Properties transactionsProperties = new Properties(defaults);
            loadPropertiesFromClasspath(transactionsProperties, TRANSACTIONS_PROPERTIES_FILE_NAME);
            //读取classpath下,jta.properties,覆盖transactions-defaults.properties、transactions.properties中相同key的值
            Properties jtaProperties = new Properties(transactionsProperties);
            loadPropertiesFromClasspath(jtaProperties, JTA_PROPERTIES_FILE_NAME);
            
            //读取通过java -Dcom.atomikos.icatch.file方式指定的自定义配置文件路径,覆盖之前的同名配置
            Properties customProperties = new Properties(jtaProperties);
            loadPropertiesFromCustomFilePath(customProperties);
            //最终构造一个ConfigProperties对象,来表示实际要使用的配置
            Properties finalProperties = new Properties(customProperties);
            return new ConfigProperties(finalProperties);
        }

配置文件优先级:transactions-defaults.properties<transactions.properties<jta.properties<自定义配置文件路径,后面的配置会覆盖之前同名key的配置。

其中transactions-defaults.properties是atomikos自带的默认配置,位于transactions-xxx.jar中.

注意不同版本的默认配置可能不同。特别是3.x版本和4.x版本的差异比较明显。

以下是4.0.6中 transactions-default.properties中配置内容,笔者对这些配置进行了归类,如下:

    ===============================================================
    ============          事务管理器(TM)配置参数       ==============
    ===============================================================
    #指定是否启动磁盘日志,默认为true。在生产环境下一定要保证为true,否则数据的完整性无法保证
    com.atomikos.icatch.enable_logging=true
    #JTA/XA资源是否应该自动注册
    com.atomikos.icatch.automatic_resource_registration=true
    #JTA事务的默认超时时间,默认为10000ms
    com.atomikos.icatch.default_jta_timeout=10000
    #事务的最大超时时间,默认为300000ms。这表示事务超时时间由 UserTransaction.setTransactionTimeout()较大者决定。4.x版本之后,指定为0的话则表示不设置超时时间
    com.atomikos.icatch.max_timeout=300000
    #指定在两阶段提交时,是否使用不同的线程(意味着并行)。3.7版本之后默认为false,更早的版本默认为true。如果为false,则提交将按照事务中访问资源的顺序进行。
    com.atomikos.icatch.threaded_2pc=false
    #指定最多可以同时运行的事务数量,默认值为50,负数表示没有数量限制。在调用 UserTransaction.begin()方法时,可能会抛出一个”Max number of active transactions reached”异常信息,表示超出最大事务数限制
    com.atomikos.icatch.max_actives=50
    #是否支持subtransaction,默认为true
    com.atomikos.icatch.allow_subtransactions=true
    #指定在可能的情况下,否应该join 子事务(subtransactions),默认值为true。如果设置为false,对于有关联的不同subtransactions,不会调用XAResource.start(TM_JOIN)
    com.atomikos.icatch.serial_jta_transactions=true
    #指定JVM关闭时是否强制(force)关闭事务管理器,默认为false
    com.atomikos.icatch.force_shutdown_on_vm_exit=false
    #在正常关闭(no-force)的情况下,应该等待事务执行完成的时间,默认为Long.MAX_VALUE
    com.atomikos.icatch.default_max_wait_time_on_shutdown=9223372036854775807
     
    ===============================================================
    =========        事务日志(Transaction logs)记录配置       =======
    ===============================================================
    #事务日志目录,默认为./。
    com.atomikos.icatch.log_base_dir=./
    #事务日志文件前缀,默认为tmlog。事务日志存储在文件中,文件名包含一个数字后缀,日志文件以.log为扩展名,如tmlog1.log。遇到checkpoint时,新的事务日志文件会被创建,数字增加。
    com.atomikos.icatch.log_base_name=tmlog
    #指定两次checkpoint的时间间隔,默认为500
    com.atomikos.icatch.checkpoint_interval=500
     
    ===============================================================
    =========          事务日志恢复(Recovery)配置       =============
    ===============================================================
    #指定在多长时间后可以清空无法恢复的事务日志(orphaned),默认86400000ms
    com.atomikos.icatch.forget_orphaned_log_entries_delay=86400000
    #指定两次恢复扫描之间的延迟时间。默认值为与com.atomikos.icatch.default_jta_timeout相同
    com.atomikos.icatch.recovery_delay=${com.atomikos.icatch.default_jta_timeout}
    #提交失败时,再抛出一个异常之前,最多可以重试几次,默认值为5
    com.atomikos.icatch.oltp_max_retries=5
    #提交失败时,每次重试的时间间隔,默认10000ms
    com.atomikos.icatch.oltp_retry_interval=10000
     
    ===============================================================
    =========          其他       =============================== ==
    ===============================================================
    java.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
    com.atomikos.icatch.client_demarcation=false
    java.naming.provider.url=rmi://localhost:1099
    com.atomikos.icatch.rmi_export_class=none
    com.atomikos.icatch.trust_client_tm=false

打印日志

4.x版本之后,优先尝试使用slf4j,如果没有则尝试使用log4j,如果二者都没有,则使用JUL。

参见:https://www.atomikos.com/Documentation/ConfiguringTheLogs

注意这里是说的是如何配置打印工作日志(work log),而前面说的是事务日志(transactions log),二者不是不同的。

本文源自:

http://www.tianshouzhi.com/api/tutorials/distributed_transaction/386

相关推荐

想减少Windows 11内存占用?请取消固定Teams

如果你想要提高Windows11系统的运行速度,那么可以禁用某些默认启用的功能和设置。如果你的Windows11是安装在已经停止支持的设备或者内存容量不高的旧设备,那么应该立即限制或禁用固...

Windows查看端口占用、查看PID对应的进程、并终止进程

Windows下:查看端口占用netstat-ano|findstr"端口号"获取到pid查看PID对应的进程tasklist|findstr"进程ID"...

计算机组成原理(36): 分时之一——进程

建立一个虚拟机VM目标:给每个程序一个自己的虚拟机“VirtualMachine”,程序并不知道其他的虚拟机。1.1进程(Process)为了捕获正在运行的程序,我们创建一个称为“进程(Proce...

window系统如何停止端口被占用的进程(高手版)

如上图1,作为开发人员是不是经常遇到这个问题?(Webserverfailedtostart.Port9527wasalreadyinuse.)当然,如果在你知道确实有某个进程正占...

电脑的文件无法删除咋回事?你需要这款神兵利器

很多朋友用电脑的时候,都遇到过文件无法删除的情况。这往往是由于文件被某个软件、进程所调用所引发的——在Windows中,某个文件如果被使用,这个文件可能就没法进行删除、重命名之类的操作了。想要进一步操...

Windows日志分析(windows 日志文件)

1.Windows日志文件简介1.1Windows日志核心分类1.系统日志系统日志包含由Windows系统组件记录的事件,记录系统进程和设备驱动程序的活动。由它审核的系统事件包括启动失败的设备驱动程...

电脑软件崩溃、闪退不用慌!DJS Tech 教你几招轻松解决

当你正全神贯注用电脑处理重要文件、沉浸在精彩的游戏世界,或是观看喜欢的视频时,软件突然崩溃、闪退,那一刻的烦躁简直难以言喻。别着急,DJSTech作为深耕计算机领域多年的专业团队,为你带来一系列超...

微软Win11推进淘汰控制面板,时间服务器配置迁移至设置应用

IT之家5月29日消息,科技媒体Winaero昨日(5月28日)发布博文,报道称微软在Windows11系统中,继续推进“淘汰控制面板”进程,配置时间服务器地址选项迁移到设置应...

微软 PowerToys更新,可帮你找出 Win11上哪些进程正在占用该文件

IT之家11月3日消息,微软针对Windows11和Windows10的PowerToys已经更新到了最新的0.64.0版本,并上线了一个名为“文件锁匠FileLock...

Windows基础操作 认识任务管理器(windows任务管理器的使用)

Windows基础操作:认识任务管理器任务管理器(TaskManager)是Windows系统中一个功能强大的实用工具,它为用户提供了实时监控系统资源、管理正在运行的程序和服务的能力。掌握任务管理器...

windows——netstat过滤(终止进程)

windows——netstat过滤(终止进程)在Windows操作系统中,使用netstat命令可以查看网络连接的状态。要过滤特定协议或端口的连接,可以使用以下命令:查看所有连接:netstat-...

只要这么做 Windows Defender与第三方就能和平共存啦

无论大家是否喜欢WindowsDefender,伴随着Windows10的不断升级,它已经成为系统的底层必备组件之一。虽然我们有各种各样的方法去关闭它,换用顺手的第三方,但只要更新打补丁,噩梦就来...

Win10如何彻底关闭wsappx进程(win10 wsappx怎么关闭)

win10如何彻底关闭wsappx进程?wsappx进程是什么?wsappx进程是Windows10系统的一部分,这个进程是WindowsStore和微软通用应用程序平台(UWP)的依赖进程。...

Windows环境黑客入侵应急与排查(黑客入侵电脑原理)

1文件分析1.1临时目录排查黑客往往可能将病毒放在临时目录(tmp/temp),或者将病毒相关文件释放到临时目录,因此需要检查临时目录是否存在异常文件。假设系统盘在C盘,则通常情况下的临时目录如下...

Windows 11 24H2 KB5044384出现大面积安装失败、任务管理器0进程等问题

Windows11KB5044384更新由于出现大量错误而无法在Windows1124H2上安装、其中包括一个奇怪的错误,即由于0x800f0922、0x800736b3和0x8...

取消回复欢迎 发表评论: