Java中seata框架的XA模式详解

 更新时间:2023年08月30日 08:45:30   作者:等後那场雪  
这篇文章主要介绍了Java中seata框架的XA模式详解,Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务,Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案,需要的朋友可以参考下

XA模式

XA 模式属于一种强一致性的事务模式。

前提

支持 XA 模式的数据库。

Java 应用通过 JDBC 访问数据库。

整体机制

在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议提供可回滚、持久化的支持,使用 XA 协议的机制来管理分支事务。

img

执行阶段

执行 XA Start、业务 SQL、XA End =》注册分支,XA Prepare => 报告分支事务的状态。

完成阶段

执行 XA Commit / XA Rollback 操作进行分支事务的提交或者回滚。

XA 模式需要 XAConnection,而获取 XAConnection 的方式有两种:

  • 方式一、要求开发者配置 XADataSource。给开发者增加了认知负担,需要为 XA 模式专门去学习和使用 XA 数据源,与透明化 XA 编程模型的设计目标相悖。
  • 方式二、根据开发者的普通 DataSource 来创建。对开发者比较友好,和 AT 模式一样,开发者完全不需要关心 XA 层面的任何问题,保持本地编程模型即可。

优先设计实现第二种方式,数据源代理根据普通数据源中获取的普通 JDBC 连接创建出相应的 XAConnection。

类比 AT 模式的数据源代理机制,如下:

ds1

但是,第二种方法有局限:无法保证兼容的正确性。

实际上,这种方法是在做数据库驱动程序要做的事情。不同的厂商、不同版本的数据库驱动实现机制是厂商私有的,我们只能保证在充分测试过的驱动程序上是正确的,开发者使用的驱动程序版本差异很可能造成机制的失效。

综合考虑,XA 模式的数据源代理设计需要同时支持第一种方式:基于 XA 数据源进行代理。

类比 AT 模式的数据源代理机制,如下:

ds2

使用方法

每个服务的 file.conf、registry.conf 配置文件的配置这里先不提供。

Business服务
|
|------> Stock服务
|
|------> Order服务 -----> Account服务

Business服务

BusinessService

@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount, boolean rollback) {
    String xid = RootContext.getXID();
    LOGGER.info("New Transaction Begins: " + xid);
    String result = stockFeignClient.deduct(commodityCode, orderCount);
    if (!SUCCESS.equals(result)) {
        throw new RuntimeException("库存服务调用失败,事务回滚!");
    }
    result = orderFeignClient.create(userId, commodityCode, orderCount);
    if (!SUCCESS.equals(result)) {
        throw new RuntimeException("订单服务调用失败,事务回滚!");
    }
    if (rollback) {
        throw new RuntimeException("Force rollback ... ");
    }
}

BusinessXADataSourceConfiguration

@Configuration
public class BusinessXADataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource dataSource() {
        return new DruidDataSource();
    }
}

Stock服务

StockBusiness

public void deduct(String commodityCode, int count) {
    String xid = RootContext.getXID();
    LOGGER.info("deduct stock balance in transaction: " + xid);
    jdbcTemplate.update("update seata_stock set count = count - ? where commodity_code = ?",
        new Object[] {count, commodityCode});
}

application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.211.55.6:3306/seata_stock?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root

StockXADataSourceConfiguration

@Configuration
public class StockXADataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }
    @Bean("dataSourceProxy")
    public DataSource dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxyXA(druidDataSource);
    }
    @Bean("jdbcTemplate")
    public JdbcTemplate jdbcTemplate(DataSource dataSourceProxy) {
        return new JdbcTemplate(dataSourceProxy);
    }
}

Order服务

OrderService

public void create(String userId, String commodityCode, Integer count) {
    String xid = RootContext.getXID();
    LOGGER.info("create order in transaction: " + xid);
    // 定单总价 = 订购数量(count) * 商品单价(100)
    int orderMoney = count * 100;
    // 生成订单
    jdbcTemplate.update("insert seata_order(user_id,commodity_code,count,money) values(?,?,?,?)",
        new Object[] {userId, commodityCode, count, orderMoney});
    // 调用账户余额扣减
    String result = accountFeignClient.reduce(userId, orderMoney);
    if (!SUCCESS.equals(result)) {
        throw new RuntimeException("Failed to call Account Service. ");
    }
}

application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.211.55.6:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root

OrderXADataSourceConfiguration

@Configuration
public class OrderXADataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }
    @Bean("dataSourceProxy")
    public DataSource dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxyXA(druidDataSource);
    }
    @Bean("jdbcTemplate")
    public JdbcTemplate jdbcTemplate(DataSource dataSourceProxy) {
        return new JdbcTemplate(dataSourceProxy);
    }
}

Account服务

AccountService

@Transactional
public void reduce(String userId, int money) {
    String xid = RootContext.getXID();
    LOGGER.info("reduce account balance in transaction: " + xid);
    jdbcTemplate.update("update seata_account set money = money - ? where user_id = ?", new Object[] {money, userId});
    int balance = jdbcTemplate.queryForObject("select money from seata_account where user_id = ?",
        new Object[] {userId}, Integer.class);
    LOGGER.info("balance after transaction: " + balance);
    if (balance < 0) {
        throw new RuntimeException("Not Enough Money ...");
    }
}

application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.211.55.6:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root

AccountXADataSourceConfiguration

@Configuration
public class AccountXADataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }
    @Bean("dataSourceProxy")
    public DataSource dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxyXA(druidDataSource);
    }
    @Bean("jdbcTemplate")
    public JdbcTemplate jdbcTemplate(DataSource dataSourceProxy) {
        return new JdbcTemplate(dataSourceProxy);
    }
    @Bean
    public PlatformTransactionManager txManager(DataSource dataSourceProxy) {
        return new DataSourceTransactionManager(dataSourceProxy);
    }
}

启动类需要标注 @EnableTransactionManagement 注解。

到此这篇关于Java中seata框架的XA模式详解的文章就介绍到这了,更多相关seata框架的XA模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Springboot Mybatis Plus自动生成工具类详解代码

    Springboot Mybatis Plus自动生成工具类详解代码

    mybatis-plus 是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生,这篇文章带你使用Springboot Mybatis Plus自动生成工具类
    2021-11-11
  • Java SwingWorkder使用实例

    Java SwingWorkder使用实例

    最近在学习Swing,我们都知道在UI表现线程里面长时间执行操作时,画面会假死,为了能够让费时操作不影响画面表现,就需要用多线程了
    2014-04-04
  • Java Socket通信(一)之客户端程序 发送和接收数据

    Java Socket通信(一)之客户端程序 发送和接收数据

    对于Socket通信简述,服务端往Socket的输出流里面写东西,客户端就可以通过Socket的输入流读取对应的内容,Socket与Socket之间是双向连通的,所以客户端也可以往对应的Socket输出流里面写东西,然后服务端对应的Socket的输入流就可以读出对应的内容
    2016-03-03
  • SpringCloud LoadBalancer自定义负载均衡器使用解析

    SpringCloud LoadBalancer自定义负载均衡器使用解析

    LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡,本文给大家深入解析 LoadBalancerClient 接口源码,感兴趣的朋友跟随小编一起看看吧
    2023-04-04
  • Java实现经典游戏推箱子的示例代码

    Java实现经典游戏推箱子的示例代码

    《推箱子》推箱子是一个古老的游戏,目的是在训练你的逻辑思考能力。本文将利用Java实现这一经典的小游戏,并采用了swing技术进行了界面化处理,需要的可以参考一下
    2022-02-02
  • Java三目运算符的实战案例

    Java三目运算符的实战案例

    三目运算符在java中运用可以说非常的广泛,下面这篇文章主要给大家介绍了关于Java三目运算符的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Java操作MongoDB插入数据进行模糊查询与in查询功能

    Java操作MongoDB插入数据进行模糊查询与in查询功能

    今天小编就为大家分享一篇关于Java操作MongoDB插入数据进行模糊查询与in查询功能,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • Java 继承与多态超详细梳理

    Java 继承与多态超详细梳理

    继承就是可以直接使用前辈的属性和方法。自然界如果没有继承,那一切都是处于混沌状态。多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作
    2022-04-04
  • SpringBoot返回html界面的使用示例

    SpringBoot返回html界面的使用示例

    本文主要介绍了SpringBoot返回html界面的使用示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11
  • Spring Cloud Gateway 使用JWT工具类做用户登录校验功能

    Spring Cloud Gateway 使用JWT工具类做用户登录校验功能

    这篇文章主要介绍了Spring Cloud Gateway 使用JWT工具类做用户登录校验的示例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01

最新评论