分布式事务解决方案——JTA

背景介绍

本地事务依赖于底层资源管理器(数据库连接),事务局限于当前事务资源内。但是随着业务发展,系统出现多数据源等,本地事务无法满足ACID,需要采用分布式事务。

分布式事务处理(JTA)

Java 事务编程接口(JTA:Java Transaction API)和 Java 事务服务 (JTS;Java Transaction Service) 为 J2EE 平台提供了分布式事务服务。
JTA架构: 包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager ) 两部分。 我们可以将资源管理器看做任意类型的持久化数据存储;事务管理器则承担着所有事务参与单元的协调与控制。 根据所面向对象的不同,我们可以将 JTA 的事务管理器和资源管理器理解为两个方面:面向开发人员的使用接口(事务管理器)和面向服务提供商的实现接口(资源管理器)。其中开发接口的主要部分即为上述示例中引用的 UserTransaction 对象,开发人员通过此接口在信息系统中实现分布式事务;而实现接口则用来规范提供商(如数据库连接提供商)所提供的事务服务,它约定了事务的资源管理功能,使得 JTA 可以在异构事务资源之间执行协同沟通。以数据库为例,IBM 公司提供了实现分布式事务的数据库驱动程序,Oracle 也提供了实现分布式事务的数据库驱动程序, 在同时使用 DB2 和 Oracle 两种数据库连接时, JTA 即可以根据约定的接口协调者两种事务资源从而实现分布式事务。正是基于统一规范的不同实现使得 JTA 可以协调与控制不同数据库或者 JMS 厂商的事务资源。

public void transferAccount() { 
        
        UserTransaction userTx = null; 
        Connection connA = null; 
        Statement stmtA = null; 
                
        Connection connB = null; 
        Statement stmtB = null; 
    
        try{ 
              // 获得 Transaction 管理对象
            userTx = (UserTransaction)getContext().lookup("\
                  java:comp/UserTransaction"); 
            // 从数据库 A 中取得数据库连接
            connA = getDataSourceA().getConnection(); 
            
            // 从数据库 B 中取得数据库连接
            connB = getDataSourceB().getConnection(); 
      
                       // 启动事务
            userTx.begin();
            
            // 将 A 账户中的金额减少 500 
            stmtA = connA.createStatement(); 
            stmtA.execute("
           update t_account set amount = amount - 500 where account_id = 'A'");
            
            // 将 B 账户中的金额增加 500 
            stmtB = connB.createStatement(); 
            stmtB.execute("\
            update t_account set amount = amount + 500 where account_id = 'B'");
            
            // 提交事务
            userTx.commit();
            // 事务提交:转账的两步操作同时成功(数据库 A 和数据库 B 中的数据被同时更新)
        } catch(SQLException sqle){ 
            
            try{ 
                  // 发生异常,回滚在本事务中的操纵
                 userTx.rollback();
                // 事务回滚:转账的两步操作完全撤销 
                //( 数据库 A 和数据库 B 中的数据更新被同时撤销)
                
                stmt.close(); 
                conn.close(); 
                ... 
            }catch(Exception ignore){ 
                
            } 
            sqle.printStackTrace(); 
            
        } catch(Exception ne){ 
            e.printStackTrace(); 
        } 
    }

SpringBoot与JTA整合

//数据源配置
@Configuration
public class DataSourceCfg {

    //jta数据源1
    @Bean(initMethod="init", destroyMethod="close", name="jtaDataSource1")
    @Primary
    @ConfigurationProperties(prefix = "ds1.jdbc")
    public DataSource jtaDataSource1() throws AtomikosSQLException {
        return new AtomikosDataSourceBean();
    }

    //jta数据源1
    @Bean(initMethod="init", destroyMethod="close", name="jtaDataSource2")
    @ConfigurationProperties(prefix = "ds2.jdbc")
    public DataSource jtaDataSource2() throws AtomikosSQLException {
        return new AtomikosDataSourceBean();
    }

    @Bean
    public JdbcTemplate ds1JdbcTemplate(@Qualifier("jtaDataSource1") DataSource jtaDataSource1) {
        return new JdbcTemplate(jtaDataSource1);
    }

    @Bean
    public JdbcTemplate ds2JdbcTemplate(@Qualifier("jtaDataSource2") DataSource jtaDataSource2) {
        return new JdbcTemplate(jtaDataSource2);
    }
}
/**
 * 事务配置类
 *
 * @author alvinkk
 * @create 2018-05-22 17:32
 **/
@Configuration
public class TransactionManagerConfig {

    @Bean
    public UserTransaction userTransaction() throws SystemException {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(10000);
        return userTransactionImp;
    }

    @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
    public TransactionManager atomikosTransactionManager() throws Throwable {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(false);
        return userTransactionManager;
    }

    @Bean(name = "transactionManager")
    @DependsOn({ "userTransaction", "atomikosTransactionManager" })
    public PlatformTransactionManager transactionManager() throws Throwable {
        UserTransaction userTransaction = userTransaction();

        JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());
        return manager;
    }
}
@Component
public class TransactionServiceImpl implements TransactionService {

    @Autowired
    @Qualifier("ds1JdbcTemplate")
    private JdbcTemplate jdbcTemplate1;

    @Autowired
    @Qualifier("ds2JdbcTemplate")
    private JdbcTemplate jdbcTemplate2;

    @Override
    @Transactional
    public void save() {
        jdbcTemplate1.execute("insert into user values ('alvin', 'sss', 12)");
        jdbcTemplate2.execute("insert into user values ('alvin11', 'sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss', 12)");
    }
}
//配置文件
ds1.jdbc:
   uniqueResourceName: ds1
   xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
   xaProperties:
     url: jdbc:mysql://10.100.1.85:3306/alvin_demo?useUnicode=true&characterEncoding=utf-8
     user: root
     password: root
   exclusiveConnectionMode: true
   minPoolSize: 3
   maxPoolSize: 10
   testQuery: SELECT 1 from dual #由于采用HikiriCP,用于检测数据库连接是否存活。

ds2.jdbc:
   uniqueResourceName: ds2
   xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
   xaProperties:
     url: jdbc:mysql://10.100.1.85:3306/alvin1_demo?useUnicode=true&characterEncoding=utf-8
     user: root
     password: root
   exclusiveConnectionMode: true
   minPoolSize: 3
   maxPoolSize: 10
   testQuery: SELECT 1 from dual #由于采用HikiriCP,用于检测数据库连接是否存活。

//测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {
    @Autowired
    private TransactionService transactionService;

    @Test
    public void testTransaction() {
        transactionService.save();
    }
} 

git地址: https://github.com/alvinlkk/springboot-learning-demo/tree/master/springboot-jta

优缺点

优点: 能够支持分布式事务
缺点: 性能开销大,不适合用于高并发场景

参考

https://www.cnblogs.com/Leo_wl/p/5728027.html https://www.ibm.com/developerworks/cn/java/j-lo-jta/

猜你喜欢

转载自my.oschina.net/alvinlkk/blog/1817370