SpringCloud Alibaba 学习教程10-seata1.3整合nacos实现分布式事务

本文详细介绍了Seata这款高性能分布式事务解决方案的特点与应用场景,并指导如何搭建Seata环境、配置服务及实现分布式事务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、介绍

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

一个分布式事务链路需要多个系统参与, 不同的系统负责不同的角色. 一般来说, 分布式事务的参与者需要包含以下 3 个角色.

TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

二、业务场景

用户进行下单操作,order-service提供创建订单接口,然后通过feign调用远程的accout-service进行账户余额扣减,再通过feign调用远程的product-service进行库存扣减操作。

在这个业务场景下,order-service、accout-service、product-service其中某一个服务发生异常就会造成分布式事务,比如fegin调用product-service时库存不足抛出了异常,那么order-service需要对订单进行回滚,account-service需要对余额进行回滚,这就会造成分布式事务的问题

三、下载最新版本及修改配置

3.1.下载seata-server

https://github.com/seata/seata/tags,这里选择v1.3.0 zip下载

3.2创建seata数据库并初始化以下3张表

CREATE 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 data
CREATE 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(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(96),
    `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;

3.3修改配置文件 conf/registry.conf文件

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "192.168.2.6:8001"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "192.168.2.6:8001"
    namespace = ""
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
  }
}

 

3.4下载config.txt 添加配置

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

放置在/conf目录下,修改文件如下内容(后续初始化至nacos后,可在nacos中修改)

service.vgroup_mapping.your−service−gruop=default,中间的{your-service-gruop}为自己定义的服务组名称,服务中的application.yaml文件里配置服务组名称。

工程中有3个服务,分别是order_service、account_service、product_service,所以配置如下

3.5下载执行脚本

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

这里注意一个地方,如果nacos不是默认端口8848,需要修改脚本文件

执行nacos-config.sh脚本

sh nacos-config.sh -h 192.168.2.6

-h 后面192.168.2.6为nacos地址

成功后提示init nacos config finished, please start seata-server

3.6登录nacos查看配置中心

3.7启动seata-server

./seata-server.bat

四、修改项目

4.1引入依赖

父pom

 <dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
	<version>2.2.0.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>
</dependency>

子pom

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
	<exclusions>
		<exclusion>
			<groupId>io.seata</groupId>
			<artifactId>seata-all</artifactId>
		</exclusion>
		<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>
</dependency>

4.2添加项目配置 

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: order_service_group
  config:
    type: nacos
    nacos:
      namespace:
      serverAddr: 192.168.2.6:8001
      group: SEATA_GROUP
      userName: "nacos"
      password: "nacos"
  registry:
    type: nacos
    nacos:
      application: seata-server
      serverAddr: 192.168.2.6:8001
      group: SEATA_GROUP
      namespace:
      userName: "nacos"
      password: "nacos"

五、启动项目测试

5.1测试前各表数据

order表

accont表

product表

5.2postman调试

从参数我们可以看出test账户对P002商品下单6个,一共300元

我们的余额满足,库存不满足,会抛出RuntimeException

5.3debug验证

order表中创建了订单

feign调用account_service扣除余额undo_log生成了记录

feign调用product_service扣除库存undo_log生成了记录

因为会库存不足,会抛出RuntimeException

放掉所有断点查看表数据是否有回滚

order表

accont表

product表

通过查看数据发现订单数据没有入库、库存、余额没有扣除,说明当出现异常时,数据有回滚,分布式事务有起作用

六、AT模式工作机制

以一个示例来说明整个 AT 分支的工作过程。

业务表:product

FieldType Key
idbigint(20) PRI
namevarchar(100)  
sincevarchar(100)  

 

 

 

  

 

AT 分支事务的业务逻辑: 

update product set name = 'GTS' where name = 'TXC';

一阶段

过程:

1、解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。

2、查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

select id, name, since from product where name = 'TXC';

 得到前镜像:

idnamesince
1TXC2014

 

 

 

3、执行业务 SQL:更新这条记录的 name 为 'GTS'。

4、查询后镜像:根据前镜像的结果,通过 主键 定位数据。

select id, name, since from product where id = 1;

得到后镜像:

idnamesince
1GTS2014

 

 

 

5、插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。

{
	"branchId": 641789253,
	"undoItems": [{
		"afterImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "GTS"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"beforeImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "TXC"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"sqlType": "UPDATE"
	}],
	"xid": "xid:xxx"
}

6、提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁 。

7、本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。

8、将本地事务提交的结果上报给 TC。

二阶段-回滚

1、收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。

2、通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。

3、数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。

4、根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

update product set name = 'TXC' where id = 1;

5、提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

二阶段-提交

1、收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。

2、异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值