shardingsphere-jdbc集成Seata分布式事务

1、导入相关依赖

复制代码
        <!-- shardingsphere-jdbc -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc</artifactId>
            <version>5.5.1</version>
        </dependency>

        <!-- shardingsphere 集成 seata AT 模式 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>2.3.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.antlr</groupId>
                    <artifactId>antlr4-runtime</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.netflix.archaius</groupId>
                    <artifactId>archaius-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

备注:

【1】shardingsphere-transaction-base-seata-at 的版本最好与 shardingsphere-jdbc 版本一致

【2】seata-all 版本最好与 seata-server 服务端版本一致

2、在应用服务的 classpath 目录下创建以下配置文件

【1】sharding-config.yml:用于支持读写分离。这里以应用已经集成了 shardingsphere-jdbc 为前提,可参考:
https://blog.csdn.net/hkl_Forever/article/details/146602740

【2】seata.conf:用于 shardingsphere-jdbc 支持 seata 分布式事务,内容案例如下:

复制代码
shardingsphere.transaction.seata.at.enable = true
shardingsphere.transaction.seata.tx.timeout = 120

client {
    application.id = order-service
    transaction.service.group = default_tx_group
}

service {
    vgroupMapping.default_tx_group = "default"
    default.grouplist = "服务IP:8091"
}

备注:

(1)事务组名称可以自定义,但要与seata服务端配置文件(seata-server.yml)中配置的事务组名称一致(否则报错),

(2)注意 default_tx_group、default 的映射关系要对应

【3】registry.conf:用于访问 seata 服务所在的注册中心和配置中心,内容案例如下:

复制代码
registry {
  type = "nacos"
  nacos {
    serverAddr = "http://nacos服务ip:8850"
    username = "xxx"
    password = "xxx"
    namespace = "xxx"
    group = "DEFAULT_GROUP"
  }
}

config {
  type = "nacos"
  nacos {
    serverAddr = "http://nacos服务ip:8850"
    username = "xxx"
    password = "xxx"
    namespace = "xxx"
    group = "DEFAULT_GROUP"
    dataId = "seata-server.yml" # Seata服务端的配置文件,一定要正确保证能访问到该文件
  }
}

【4】在集成 seata 服务对应的数据库中创建 undo_log 表,(此为必须,否则全局事务无法回滚),可参考:
https://blog.csdn.net/hkl_Forever/article/details/145803842

3、传递 TX_XID(全局事务id)

【1】在调用方的服务中配置openfeign的接口 RequestInterceptor 的实现类进行传递

复制代码
@Configuration
public class TransferTxXidInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        //---------------------- 传递 seata 的 TX_XID  start ---------------------
        String xid = RootContext.getXID();
        if (StrUtil.isNotBlank(xid)) {
            requestTemplate.header(RootContext.KEY_XID, xid);
        }
        //---------------------- 传递 seata 的 TX_XID  end ---------------------
    }

}

【2】在被调用方的服务中配置 seata 支持的事务传播拦截器,获取调用方传递过来的 TX_XID

复制代码
@Configuration
public class JakartaSeataConfig {

    /**
     * <p>微服务事务传播拦截器(适用SpringBoot 3.x)</p>
     */
    @Bean
    public JakartaSeataWebMvcConfigurer getJakartaSeataWebMvcConfigurer() {
        return new JakartaSeataWebMvcConfigurer();
    }

//    /**
//     * <p>微服务事务传播拦截器(适用SpringBoot 2.x)</p>
//     */
//    @Bean
//    public SeataWebMvcConfigurer getSeataWebMvcConfigurer() {
//        return new SeataWebMvcConfigurer();
//    }

}

4、验证测试,启用微服务应用

调用方代码案例

复制代码
	@Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrder(AddOrderReq data) {
        if (ObjUtil.isNull(data)) {
            return;
        }

        //保存订单
        Order order = BeanUtil.copyProperties(data, Order.class);
        order.setOrderNo("S-" + IdUtil.getSnowflake().nextIdStr());
        order.setOrderTotalPrice(ObjUtil.defaultIfNull(data.getOrderTotalPrice(), NumberUtil.toBigDecimal(0.00)));
        this.save(order);

        //记录支付流水
        InsPaymentFlowReq insPaymentFlowReq = new InsPaymentFlowReq();
        insPaymentFlowReq.setOrderNo(order.getOrderNo());
        insPaymentFlowReq.setCostPrice(order.getOrderTotalPrice());
        insPaymentFlowReq.setRemark(order.getRemark());
        paymentFlowClient.insPaymentFlow(insPaymentFlowReq);

        ThrowUtil.fail("order服务出错了!");
    }

被调用方代码案例

复制代码
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void insPaymentFlow(InsPaymentFlowReq data) {
        if (ObjUtil.isNull(data)) {
            return;
        }
        PaymentFlow paymentFlow = BeanUtil.copyProperties(data, PaymentFlow.class);
        paymentFlow.setFlowNo("F-" + IdUtil.getSnowflake().nextIdStr());
        this.save(paymentFlow);

        //ThrowUtil.fail("payment服务出错了!");
    }

经测试验证后,在 shardingsphere-jdbc 读写分离的前提下,服务调用链路中有报错双方都可以正常回滚,符合预期

5、总结注意

【1】shardingsphere-jdbc 集成 seata 与 单数据源集成 seata 完全是各自独立的方式(不搭嘎)

【2】shardingsphere-jdbc 集成 seata 后,切记在主方法上要使用 @Transactional 不能使用 @GlobalTransactional。单数据源集成 seata 则在主方法上使用 @GlobalTransactional 即可

【3】shardingsphere-jdbc 集成 seata 后,如果只使用读写分离场景没问题。但如果使用分片、分库分表场景则 seata 事务不靠谱(不建议分片场景和seata一起使用)

相关推荐
罗超驿12 分钟前
8.数据库约束学习笔记:从非空、默认、唯一与主键约束到主键自增
数据库·mysql
阿维的博客日记1 小时前
Spring Cloud 为什么需要服务注册与发现中心这些东西?
后端·spring·spring cloud
Irene19913 小时前
SQL示例:分别使用 MySQL 和 Oracle 创建表,MySQL 插入数据建索引(自增主键、指定主键的区别,VARCHAR,VARCHAR2)
mysql·oracle
用户4217632814073 小时前
如何用java实现一个简单的并发版本控制MVCC
mysql
阿维的博客日记4 小时前
求解深分页问题,last pk适合什么情况
java·mysql·深分页
__water4 小时前
【下载配置Mysql】
mysql
DolphinScheduler社区4 小时前
Apache DolphinScheduler 与 Spring Cloud Data Flow:差异与优势解析
spring·spring cloud·apache·海豚调度·大数据工作流调度
Dxy12393102167 小时前
MySQL 连表查询更新:从理论到实践
数据库·mysql
阿丰资源7 小时前
基于Springboot+mysql的在线兼职平台(附源码)
spring boot·后端·mysql
怪祝浙7 小时前
从简单项目入手Java(学生系统)V6(Web版本 Spring Boot3 MySQL Vue3 MyBatis)
java·spring boot·mysql