服务编排解决分布式事务_什么是分布式事务

(1) 2024-09-03 13:23

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
服务编排解决分布式事务_什么是分布式事务,希望能够帮助你!!!。

一、Seata简介

1.1介绍

Seata 是⼀款开源的分布式事务框架。致⼒于在微服务架构下提供⾼性能和简单易⽤的分布式事务服务。在 Seata 开源之前,Seata 对应的内部版本在阿⾥经济体内部⼀直扮演着分布式⼀致性中间件的⻆⾊,帮助经济体平稳的度过历年的双11,对各业务单元业务进⾏了有⼒的⽀撑。经过多年沉淀与积累,商业化产品先后在阿⾥云、⾦融云进⾏售卖。2019.1 为了打造更加完善的技术⽣态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备。

官网地址:https://seata.io/zh-cn/index.html

发展历程:

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第1张

seata的github地址:https://github.com/seata/seata

1.2核心功能

1. 微服务框架⽀持:⽬前已⽀持 Dubbo、Spring Cloud、Sofa-RPC、Motan 和 grpc 等RPC框架,其他框架持续集成中

2. AT模式:提供⽆侵⼊⾃动补偿的事务模式,⽬前已⽀持 MySQL、 Oracle 、PostgreSQL和 TiDB的AT模式,H2 开发中

3. TCC 模式:⽀持 TCC 模式并可与 AT 混⽤,灵活度更⾼

4. SAGA模式:为长事务提供有效的解决⽅案

5. XA 模式:⽀持已实现 XA 接⼝的数据库的 XA 模式

6. 高可用:支持基于数据库存储的集群模式,⽔平扩展能⼒强

1.3Seata核心概念

Seata中有三⼤模块,分别是 TM、RM 和 TC。其中 TM 和 RM 是作为 Seata 的客户端与业务系统集

成在⼀起,TC 作为 Seata 的服务端独⽴部署。

TC (Transaction Coordinator) - 事务协调者:维护全局和分⽀事务的状态,驱动全局事务提交或回滚。【就是Seata Server】

TM (Transaction Manager) - 事务管理器:定义全局事务的范围:开始全局事务、提交或回滚全局事务。【就是事务发起者】

RM (Resource Manager) - 资源管理器:管理分⽀事务处理的资源,与TC交谈以注册分⽀事务和报告分⽀事务的状态,并驱动分⽀事务提交或回滚。【所有事务的参与者】

图片来自官方

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第2张

说明:在 Seata 中,分布式事务的执⾏流程:

  • TM 开启分布式事务, TM会 向 TC 注册全局事务记录;
  • 操作具体业务模块的数据库操作之前, RM 会向 TC 注册分⽀事务;
  • 当业务操作完事后.TM会通知 TC 提交/回滚分布式事务;
  • TC 汇总事务信息,决定分布式事务是提交还是回滚;
  • TC 通知所有 RM 提交/回滚 资源,事务⼆阶段结束。

二、Seata服务器搭建

2.1、SeataServer改造

TC 事务协调器也就是Seata Server,下载地址https://github.com/seata/seata/releases

Seata Server 要向注册中⼼进⾏注册,这样,其他服务就可以通过注册中⼼去发现 SeataServer,与 SeataServer进⾏通信。

Seata ⽀持多款注册中⼼服务:nacos 、eureka、redis、zk、consul、etcd3、sofa。

在这里演示使⽤ nacos注册中⼼,nacos服务的连接地址、注册的服务名,这需要在seata/conf/registry.conf⽂件中进行配置:

修改conf/registry.conf

注册中心

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第3张

配置中心

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第4张

添加配置到nacos

下载配置config.txt https://github.com/seata/seata/tree/develop/script/config-center添加到nacos,比较多。在官网的手册中https://seata.io/zh-cn/docs/user/configurations.html针对每个⼀项配置介绍。

config.txt配置文件说明:主要配置Server端存储的模式(store.mode)现有file,db,redis三种。主要存储全局事务会话信息,分⽀事务信息, 锁记录表信息,seata-server默认是file模式。file只能⽀持单机模式, 如果想要⾼可⽤模式的话可以切换db或者redis,这里采用db的方式来进行全局事务的演示。

修改config.txt配置文件:

#存储模式store.mode=db#mysql的连接信息store.db.datasource=druidstore.db.dbType=mysqlstore.db.driverClassName=com.mysql.jdbc.Driverstore.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=truestore.db.user=rootstore.db.password=adminstore.db.minConn=5store.db.maxConn=30store.db.globalTable=global_tablestore.db.branchTable=branch_tablestore.db.queryLimit=100store.db.lockTable=lock_tablestore.db.maxWait=5000

如果手动导入的话相当的多,这里使用官方提供的脚本进行导入到nacos配置中心

脚本下载地址:https://github.com/seata/seata/tree/develop/script/config-center/nacos

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第5张

打开git bash here 执⾏nacos-config.sh。【注意这里将config.txt放到根目录下边,nacos-config.sh放到conf目录下边,操作环境是windows如果是linux直接输入命令即可。】

执行命令

sh nacos-config.sh -h 127.0.0.1

创建seata server所使用的表

需要在你的数据库中创建一个seata的数据库,专门用于存储seata的数据信息。建表语句https://github.com/seata/seata/tree/develop/script/server/db 下载mysql的即可。

-- -------------------------------- The script used when storeMode is 'db' ---------------------------------- the table to store GlobalSession dataCREATE TABLE IF NOT EXISTS `global_table`( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;-- the table to store BranchSession dataCREATE TABLE IF NOT EXISTS `branch_table`( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;-- the table to store lock dataCREATE TABLE IF NOT EXISTS `lock_table`( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(128), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;

启动seataServer

bin/seata-server.bat

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第6张

三、Seata-AT模式应用

3.1 AT模式介绍

AT模式是⼀种无侵入的分布式事务解决⽅案。在AT模式下,用户只需关注⾃⼰的“业务 SQL”,⽤户

的 “业务 SQL” 作为⼀阶段,Seata 框架会⾃动⽣成事务的⼆阶段提交和回滚操作。

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第7张

3.2AT模式原理

在介绍AT 模式的时候它是⽆侵⼊的分布式事务解决⽅案, 那么如何做到对业务的⽆侵⼊的呢?

1.⼀阶段:在⼀阶段,Seata 会拦截“业务 SQL”,⾸先解析 SQL 语义,找到“业务 SQL”要更新的业务数

据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在⼀个数据库事务内完成,这样保证了⼀阶段操作的原子性。

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第8张

3.二阶段

提交:⼆阶段如果是提交的话,因为“业务 SQL”在⼀阶段已经提交至数据库, 所以 Seata 框架只需将⼀阶段保存的快照数据和⾏锁删掉,完成数据清理即可。

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第9张

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第10张

回滚:⼆阶段如果是回滚的话,Seata 就需要回滚⼀阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要⾸先要校验脏写,对⽐“数据库当前业务数据”和 “after image”,如果两份数据完全⼀致就说明没有脏写,可以还原业务数据,如果不⼀致就说明有脏写,出现脏写就需要转⼈⼯处理。

AT模式的⼀阶段、⼆阶段提交和回滚均由Seata框架⾃动⽣成,⽤户只需编写“业务SQL”,便能轻松接⼊分布式事务,AT 模式是⼀种对业务⽆任何侵⼊的分布式事务解决⽅案。

3.3应用

1、 TM/RM端整合Seata

TM事务管理者就是事务发起者,RM资源管理者就是事务参与者,这里就是将咱们的业务系统中添加seata。

首先在各自的业务库【所有的RM微服务】中添加undo_log表。

DDL

-- 注意此处0.3.0+ 增加唯⼀索引 ux_undo_logCREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

TM/RM端整合seata⼀共有五个步骤

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第11张

事务分组的概念

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第12张

说明:事务分组有点类似于Nacos中的分组概念,分组之后有一个宕机可以快速切换到另外一个Group中。

添加依赖

在aiispringcloudalibaba- 2.1.0.RELEASE组件中默认使用的是0.7版本的seata版本比较低在这里更换seata的版本。

父POM中添加依赖管理

<!--seata版本管理, ⽤于锁定⾼版本的seata --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.3.0</version> </dependency> 子POM中添加 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency> <!--seata版本管理, ⽤于锁定⾼版本的seata --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </dependency>

建议:这里最好创建一个公共的依赖,直接在公共依赖中添加依赖和配置文件即可。

添加registry.conf配置

registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" loadBalance = "RandomLoadBalance" loadBalanceVirtualNodes = 10 nacos { application = "seata-server" serverAddr = "192.168.198.10:8848" group = "DEFAULT_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } } config { # file、nacos 、apollo、zk、consul、etcd3 type = "nacos" nacos { serverAddr = "192.168.198.10:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } }

添加seata配置

#在nacos中有配置spring: cloud: alibaba: seata: tx-service-group: my_test_tx_group#日志输出级别logging: level: io: seata: debug

注意:我是在公共模块的application-seata.yml配置文件中添加的配置,需要在各个RM和TM中激活这个配置文件

profiles: active: seata

配置代理数据源

@Configuration public class DataSourceConfig { /** * 使⽤druid连接池 */ @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource() { return new DruidDataSource(); } @Primary //设置⾸选数据源对象 @Bean("dataSource") public DataSourceProxy dataSource(DataSource druidDataSource) { return new DataSourceProxy(druidDataSource); } } 启动扫描配置类,分别加载每个⼯程的启动类中 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @Import(DataSourceConfig.class)

开启全局事务

在TM业务方法上贴上@GlobalTransactional

2、遇到的坑

1)在TM和RM中配置registry.conf的时候,没有写application导致没办法跟seataServer通信

2)在TM模块如果引入公共模块的话也需要激活

四、Seata-TCC模式应用

4.1TCC模式介绍

介绍

Seata 开源了 TCC 模式,该模式由蚂蚁⾦服贡献。TCC 模式需要⽤户根据⾃⼰的业务场景实现Try、Confirm 和 Cancel 三个操作;事务发起⽅在⼀阶段 执⾏ Try ⽅式,在⼆阶段提交执⾏ Confirm⽅法,⼆阶段回滚执⾏ Cancel ⽅法。http://seata.io/zh-cn/docs/overview/what-is-seata.html 官网参考文档。

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第13张

TCC 三个⽅法描述:

  • Try:资源的检测和预留;
  • Confirm:执⾏的业务操作提交;要求 Try 成功 Confirm ⼀定要能成功;
  • Cancel:预留资源释放。

业务模型分 2 阶段设计:

用户接⼊ TCC ,最重要的是考虑如何将⾃⼰的业务模型拆成两阶段来实现。

以“扣钱”场景为例,在接⼊ TCC 前,对A账户的扣钱,只需⼀条更新账户余额的 SQL 便能完成;但是在接⼊ TCC 之后,⽤户就需要考虑如何将原来⼀步就能完成的扣钱操作,拆成两阶段,实现成三个⽅法,并且保证⼀阶段 Try 成功的话 ⼆阶段 Confirm ⼀定能成功。

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第14张

Try 方法作为⼀阶段准备⽅法,需要做资源的检查和预留。在扣钱场景下,Try 要做的事情是就是检查账户余额是否充⾜,预留转账资⾦,预留的⽅式就是冻结 A 账户的 转账资⾦。Try ⽅法执⾏之后,账号 A 余额虽然还是 100,但是其中 30 元已经被冻结了,不能被其他事务使⽤。

⼆阶段 Confirm ⽅法执⾏真正的扣钱操作。Confirm 会使⽤ Try 阶段冻结的资⾦,执⾏账号扣款。Confirm 方法执行之后,账号 A 在⼀阶段中冻结的 30 元已经被扣除,账号 A 余额变成 70 元 。

如果⼆阶段是回滚的话,就需要在 Cancel ⽅法内释放⼀阶段 Try 冻结的 30 元,使账号 A 的回到初始状态,100 元全部可用。

总结

用户接入TCC 模式,最重要的事情就是考虑如何将业务模型拆成2阶段,实现成 TCC 的 3 个⽅法,并且保证 Try 成功 Confirm ⼀定能成功。相对于 AT 模式,TCC模式对业务代码有⼀定的侵⼊性,但是 TCC 模式⽆ AT 模式的全局行锁,TCC 性能会⽐ AT 模式高很多。

4.2TCC使用Demo

1、针对RM端改造

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第15张

针对RM端,实现起来需要完成try/commit/rollback的实现,所以步骤相对较多但是前三步骤和AT模式⼀样

不同的是TCC模式不在使用全局代理数据源,而是直接对业务代码进行改造。

接口改造

/** * @LocalTCC 该注解需要添加到上⾯描述的接⼝上,表示实现该接⼝的类被 seata 来管理,seata 根据事务的状态, * ⾃动调⽤我们定义的⽅法,如果没问题则调⽤ Commit ⽅法,否则调⽤ Rollback ⽅法。 */ @LocalTCC public interface OrderService { /** * @TwoPhaseBusinessAction 描述⼆阶段提交 * name: 为 tcc⽅法的 bean 名称,需要全局唯⼀,⼀般写⽅法名即可 * commitMethod: Commit⽅法的⽅法名 * rollbackMethod:Rollback⽅法的⽅法名 * @BusinessActionContextParamete 该注解⽤来修饰 Try⽅法的⼊参, * 被修饰的⼊参可以在 Commit ⽅法和 Rollback ⽅法中通过BusinessActionContext 获取。 */ @TwoPhaseBusinessAction(name = "addTcc", commitMethod = "addCommit", //提交代码 rollbackMethod ="addRollBack"//回滚代码 ) void add(@BusinessActionContextParameter(paramName = "order") Orders order); /** * 提交代码 * 入参必须是BusinessActionContext */ boolean addCommit(BusinessActionContext context); /** * 添加回滚代码 * 入参必须是BusinessActionContext */ boolean addRollBack(BusinessActionContext context); }

实现类

@Service public class OrderServiceImpl implements OrderService { @Override public void add(Orders order) { //执行保存 //重点是需要在业务表中添加字段 来表明是事务未提交的中间状态 } @Override public boolean addCommit(BusinessActionContext context) { ///这个key就是 接口中的入参的key Object jsonOrder = context.getActionContext("order"); Object order = JSON.parseObject(jsonOrder.toString(), Orders.class); //todo 修改中间状态,并且可以执行一些成功的业务逻辑 //返回true代表执行成功 return true; } @Override public boolean addRollBack(BusinessActionContext context) { //todo 执行回滚逻辑 //返回true 代表执行成功 return true; } }

类比

其他的RM资源微服务也需要按照这样子去修改。

4.3TCC模式使用总结

TCC模式相比AT模式的话,TCC需要对业务代码进行侵入,并且需要对业务表添加中间状态。

TCC模式难点是将业务拆分成二阶段提交。

TCC模式的优点也和明显是没有全局行锁,效率比较高。

五、Seata-Saga模式

5.1saga模式简介

Saga 模式是 Seata 开源的长事务解决⽅案,将由蚂蚁⾦服主要贡献。在 Saga 模式下,分布式事务内有多个参与者,每⼀个参与者都是⼀个冲正补偿服务,需要⽤户根据业务场景实现其正向操作和逆向回滚操作。

分布式事务执⾏过程中,依次执行各参与者的正向操作,如果所有正向操作均执⾏成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执⾏前⾯各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

服务编排解决分布式事务_什么是分布式事务_https://bianchenghao6.com/blog__第16张

适⽤场景:

  • 业务流程⻓、业务流程多
  • 参与者包含第三⽅公司或遗留系统服务,⽆法提供 TCC 模式要求的三个接⼝
  • 典型业务系统:如⾦融⽹络(与外部⾦融机构对接)、互联⽹微贷、渠道整合等业务系统

5.2三种模式对比

模式

集成难度

隔离性

推荐度

数据库改造

实现机制

场景

AT

保证

UNDO_LOG

DataSource代理

⾃研项⽬全场景、拥有数据访问权限、快速集成场景

TCC

⾮常⾼

保证

TCC实现

更⾼的性能要求、更复杂的场景

Sage

中等

不保证

流程与实例表

状态机

⻓流程、涉及⼤量第三⽅调⽤

AT 模式是⽆侵⼊的分布式事务解决⽅案,适⽤于不希望对业务进⾏改造的场景,⼏乎0学习成本。

TCC 模式是⾼性能分布式事务解决⽅案,适⽤于核⼼系统等对性能有很⾼要求的场景。

Saga 模式是⻓事务解决⽅案,适⽤于业务流程⻓且需要保证事务最终⼀致性的业务系统,Saga 模式⼀阶段就会提交本地事务,⽆锁,⻓流程情况下可以保证性能,多⽤于渠道层、集成层业务系统。事务参与者可能是其它公司的服务或者是遗留系统的服务,⽆法进⾏改造和提供 TCC 要求的接⼝,也可以使⽤Saga 模式。

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复