seata+nacos 实现 AT 模式分布式事务

本贴最后更新于 1214 天前,其中的信息可能已经时移俗易

1、AT 模式简介

AT 模式官网已经给出了很详细的介绍,可以直接看官网

http://seata.io/zh-cn/docs/dev/mode/at-mode.html

2、建立项目

涉及的代码过多,这里只对几个关键的步骤进行说明,完整代码可以到 git 上下载,AT 模式在 master 分支上,数据库脚本在 script 目录下

https://gitee.com/WylLoveX/seata.git

2.1、maven 依赖

这里我们建立一个 spring boot 项目,基于 2.2.5.RELEASE 版本,引入 seata 和 nacos 需要的依赖。

spring-cloud-starter-alibaba-seata 内部封装了 seata 分布式事务的 XID 的传递,引入直接使用,如果不用这个组件,就只能自己解决 XID 传递的问题;

seata-spring-boot-starter 的版本号和 seata 版本保持一致;

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.2.5.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2.2.5.RELEASE</version> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.3.0</version> <exclusions> <exclusion> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2.2.5.RELEASE</version> </dependency> </dependencies>

2.2、yml 配置

先配置将服务注册到 nacos,然后对 seata 进行配置,seata 的 tx-service-group 的值,对应了 seata 事务组的配置,例如,我这个服务的 seata.tx-service-group 的值为 order_group,那就要和 seata 中的配置对应

service.vgroupMapping.order_group=default

server: port: 8080 spring: datasource: url: jdbc:mysql://10.116.11.110:3306/order?useUnicode=true&characeterEncoding=utf-8&serverTimezone=UTC&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver application: name: order cloud: nacos: discovery: server-addr: 10.116.11.110:8848 logging: level: io: seata: debug mybatis: # Mybatis配置Mapper路径 mapper-locations: classpath:mapper/**/*.xml seata: tx-service-group: order_group config: type: nacos nacos: server-addr: 10.116.11.110:8848 group: SEATA_GROUP

2.3、数据源配置

然后我们需要设置 mybatis 使用 seata 的数据源代理

import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; /** * @author Mr.Wen * @version 1.0 * @date 2021-10-27 19:06 */ @Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapper-locations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDatasource(){ return new DruidDataSource(); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource datasource) throws Exception{ SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(new DataSourceProxy(datasource)); sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); return sessionFactoryBean.getObject(); } }

2.4、undo_log

AT 模式下,每个服务对应的库中,都要建立 undo_log 表

-- for AT mode you must to init this sql for you business database. the seata server not need it. CREATE TABLE IF NOT EXISTS `undo_log` ( `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id', `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization', `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status', `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime', `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime', UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

2.5、项目启动

启动需要几个注解,因为我们自己建立了数据源,所以就直接剔除 spring boot 的数据源自动配置

/** * @author Mr.Wen * @version 1.0 * @date 2021-10-21 16:59 */ @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @Import(DataSourceProxyConfig.class) public class OrderApplication { public static void main(String[] args){ SpringApplication.run(OrderApplication.class); } }

3、使用

事务发起端添加全局事务注解 @GlobalTransactional,同时注意不要捕获异常,否则事务不会回滚

事务发起端:

image.png

事务参与:

image.png

image.png

4、注意

这个模式的核心是 undo_log 表,这个表记录的操作的数据在事务提交前的状态和事务提交后的状态,本地事务会将 undo_log 和我们的数据库操作一起提交,所以,我们在 3 中的全局事务发起者中打个断点,会发现事务已经是提交了的,出错回滚的话,其实并不是我们理解的 rollback,因为我们修改数据的事务已经提交了,seata 的回滚就是读取 undo_log 表的操作日志,找到对应数据的事务提交前的记录,将他重新设置为事务提交前的样子。全局事务回滚或者提交后,会删除 undo_log 中对应的记录。

分布式事务如果回滚失败,可以检查下 XID 是否传递成功。

  • 开发
    41 引用 • 159 回帖 • 2 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖
  • Nacos
    23 引用 • 4 回帖 • 1 关注
  • Seata
    4 引用
1 操作
wenyl 在 2021-11-03 11:45:29 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...