【SpringCloud】使用Seata实现分布式事务

本文详细介绍了Seata框架在分布式场景下的需求背景,包括Seata的构成(TC,TM,RM),以及AT模式的事务处理流程。着重展示了如何部署SeataServer,配置数据库和事务管理,并通过实战演示了如何在项目中集成Seata以实现数据一致性,特别是无侵入的AT模式应用示例。
摘要由CSDN通过智能技术生成

一、Seata 框架的需求背景

一般情况下,在 Java 项目中如果存在对一个数据库实例的一系列操作的,为了避免程序执行过程中出现异常,我们通常会使用一个事务包裹住这一系列数据库操作,从而保证这一组操作的数据安全。我们通常使用Spring工具箱中的工具来实现本地事务(例如 @Transactional 注解提供的声明式事务功能等)。
但是,在微服务架构下,一个微服务可能存在多个实例,或者说一个微服务实例在运行期间可能同时对多个数据库实例进行CRUD。在这种分布式应用场景下,使Spring工具箱内的工具就失效了。我们需要借助分布式事务来保证分布式应用场景下的数据一致性。Seata 提供了分布式事务完备的解决方案,针对不同的分布式事务场景有不同的模式。

二、Seata 事务模式与架构

2.1 Seata 组成

Seata 框架由三个组件组成,分别是事务协调器(Transaction Coordinator,简称TC),事务管理器(Transaction Manager,简称TM),资源管理器(Resource Manager,简称RM)。

  • 事务协调器(TC):是一个中心化的事务协调者的角色,主要用来协调全局事务的提交和回滚,并管理全局事务与分支事务的状态。是一个需要独立部署的服务,也就是我们下面要进行部署的 Seata Server;
  • 事务管理器(TM):主要用来发起一个全局事务,对一个全局事务是提交,还是回滚做出决议。在我们实际的项目中,
  • 资源管理器(RM):操作分支事务进行提交或者回滚,并向事务协调器(TC)上报分支事务的状态。

在这里插入图片描述
以上图为例来介绍下 Seata 的架构。 事务协调器(TC,又被叫做 Seata Server)是需要被独立部署的,在AT 模式下,TM 和 RM 则是在相应的微服务中引入依赖,添加相应的配置,在需要发起全局事务的方法上添加相应的注解即可。

  • 上图在 Business 微服务模块中发起一个全局事务,并将该全局事务注册到 TC 上;
  • Business 微服务模块调用了 Stock 微服务和 Order 微服务,Stock 微服务和 Order 微服务都属于该全局事务下的分支事务,这2个被调用的微服务执行本地事务并生成相应的回滚日志到undolog数据库表中,执行完成之后(无论成功或失败)也会向 TC 上报分支事务执行情况。
  • 在 Order 微服务中又调用了 Account 微服务,又开启了一个分支事务,Account 微服务执行本地事务并生成相应的回滚日志到undolog数据库表中,执行完毕之后向 TC 上报自己所在的分支事务执行状态。
  • Business 微服务对全局事务的执行结果进行决议,如果所有的分支事务都执行成功,则提交全局事务,否则,会根据undolog数据库表中记录的回滚日志,对所有执行成功的分支事务进行回滚。

2.2 Seata 事务模式

模式类型数据一致性性能业务侵入性适用场景
AT 模式弱一致性无侵入大多数分布式事务场景,要求无须改造业务代码,快速接入时
XA 模式强一致性无侵入想要迁移到 Seata 平台基于 XA 协议的老应用,使用 XA 模式将更平滑
Saga 模式最终一致性很高有侵入,需要实现状态机及补偿代码业务流程长、业务流程多,或者遗留系统服务,无法提供TCC要求的3个接口
TCC 模式弱一致性很高有侵入,需要实现try,confirm,cancel 3个接口适用于核心系统等对性能有很高要求的场景

三、Seata 实战演示

下面演示 AT 模式下的 Seata 使用。分为2各部分:部署 Seata Server,分布式事务的数据一致性。

3.1 部署 Seata Server

3.1.1 下载 Seata Server

在 Seata 的 Github 主页的 Release 页面下载对应版本的二进制压缩包。
在这里插入图片描述

3.1.2 更改 Seata Server 配置

默认的 Seata Server 无法运行,需要更改配置,这里的 Seata Server 将使用 MySQL 数据库作为数据源,使用 Nacos 作为服务发现、注册中心。
首先,在Seata Server 的conf 目录下找到文件 file.conf.example 复制一份,并将文件名改为 file.conf,打开该文件进行更改,改动主要集中在 store 节点:

store {
  ## store mode: file、db
  mode = "db"
  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "druid"
    ## 配置的数据库类型
    dbType = "mysql"
    ## 数据库连接驱动
    driverClassName = "com.mysql.jdbc.Driver"
    ## 数据库连接 URL
    url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
    ## 数据库连接账号密码
    user = "fuyou"
    password = "fuyou3323"
    ## 数据库连接配置
    minConn = 5
    maxConn = 50
    ## Seata Server在数据库表中存放全局事务锁信息的表名
    globalTable = "global_table"
    ## Seata Server在数据库表中存放分支事务锁信息的表名
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
  }
}

以上配置的主要更改点是,把mode字段取值改为db,然后就是数据库相关的配置,设置数据库类型,数据库驱动,数据库连接的URL,数据库的账号密码,以及 Seata Server 存储全局锁及表所的数据库表名。

接下来接着更改 registry.conf 文件里的配置,内容大概如下:

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

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "myGroup"
    namespace = "public"
    cluster = "default"
    username = ""
    password = ""
  }
}

因为我们使用 Nacos 进行服务的注册发现,因此我们将Seata Server也注册到 Nacos 上进行服务管理和使用(当然,你也可以选择其他类型的注册中心)。上述配置主要是配置了 注册中心的类型,Seata Server的服务名,Nacos的地址、服务组等信息。

3.1.3 创建 Seata Server 所需的数据库、数据库表

我们在上面配置了 Seata Server 中使用MySQL数据库进行存储,并在 URL 中配置了 Seata Server 使用的数据库库名为 seata。Seata Server(TC)在运行期间,需要管理协调全局事务和分支事务,在这个过程需要把一些信息存在数据库中,例如XID(全局事务ID),分支事务ID等信息。
为了避免资源的竞争,Seata也会为资源加锁,避免不同的全局事务之间抢占资源。例如,一个分支事务只有在获取到全局锁和本地锁的情况下才可以提交成功对应的分支事务,并将对应的回滚日志写到数据库中。全局锁和本地锁的相关信息就存在名为 seata 的数据库的 lock_table 表中。

-- the table to store GlobalSession data
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 = utf8mb4;

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

-- 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 = utf8mb4;

本地创建库表情况如下:
在这里插入图片描述

除了 Seata Server 要使用的数据库及数据库表以外,还需要在引入 Seata 依赖的 client 侧的微服务所使用的数据库内,创建一个数据库表undo_log,这个就是 Seata 的回滚日志表,每个执行成功的分支事务都需要将该分支对应的回滚日志写入到该表中,如果全局事务回滚时,每个分支事务就根据分支事务的回滚日志进行恢复。

CREATE TABLE IF NOT EXISTS `undo_log`
(
    `id`            BIGINT(20)   NOT NULL AUTO_INCREMENT COMMENT 'increment id',
    `branch_id`     BIGINT(20)   NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(100) 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     NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME     NOT NULL COMMENT 'modify datetime',
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
    AUTO_INCREMENT = 1
    DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
3.1.4 启动 Seata Server

在解压好的Seata Server 目录内,切换到 /bin 目录下,运行 Seata Server 的启动脚本。
在这里插入图片描述
启动成功后,效果大致如下:

在这里插入图片描述

PS :
1. 因为 Seata 要注册到 Nacos 上,所以在启动Seata Server之前务必先确保Nacos服务在线(你也可以选择其他类型的注册中心,因为我使用的是Nacos,所以需要确保Nacos在线);
2. 尽量确保 seata server 的 /lib 目录内依赖的mysql 驱动的版本与本地安装的数据库版本一致,或者相近,否则可能会有报错;

3.2 Seata AT 模式分布式事务数据一致性演示

3.2.1 添加依赖

接下来我们要演示 Seata 的 AT 模式下可以保证分布式事务的数据一致性。对于 Seata 来说微服务侧是 client 端,我们首先需要先添加 Seata 依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
3.2.2 在项目中配置 Seata

在需要使用Seata的每个微服务的 application.yaml 文件中添加 Seata 的配置项:

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group: seata-server-group

seata:
  application-id: order
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: localhost:8848
      namespace: dev
      group: myGroup
      cluster: default
  service:
    vgroup-mapping:
      seata-server-group: default
3.2.2 项目中使用 Seata AT 模式

因为 Seata 的 AT 模式是无侵入的,我们只需要在开启分布式事务的方法上添加 @GlobalTransactional 注解即可。示例代码如下:

	@DeleteMapping("order")
    @GlobalTransactional(name = "business", rollbackFor = Exception.class)
    public void deleteOrder(@RequestParam("orderId") Long orderId) {
        customerService.deleteOrder(orderId);
    }

	@Override
    @Transactional
    public void deleteOrder(Long orderId) {
        stockService.recoverStock(orderId);
        orderService.deleteOrder(orderId);
        // 执行到此处显式抛出异常
        throw new RuntimeException("Branch transaction exception.");
    }

在以上演示代码的 deleteOrder 方法中,调用了 stockService 服务的 deleteOrder,此处是一个 openFeign 的远程调用。不属于本地的事务。如果调用该方法后,还发现订单未删除,说明分布式事务生效了,该全局事务下的所有分支事务都进行了回滚。

我在本地新下了一个订单,系统生成的订单ID 为 8.

在这里插入图片描述

然后调用上述代码中提供的订单删除接口。
在这里插入图片描述

再次查询订单ID 为 8 的订单,发现并该订单还在。
在这里插入图片描述
以上就是 seata 框架 AT 模式下的演示。Seata 的 AT模式对业务无侵入,不用对项目代码进行改造,只需要在对应位置处加上相应注解即可。改造工作量很小。

Spring Cloud Alibaba Seata 是一款优秀的分布式事务解决方案,它可以帮助开发人员在分布式环境下处理复杂的事务操作。在分布式系统,由于多个服务之间存在依赖关系,因此需要对服务之间的事务进行协调和管理,以确保数据的一致性和完整性。以下是对Spring Cloud Alibaba Seata处理分布式事务的需求分析: 1. 事务管理:在分布式系统,需要对多个服务之间的事务进行管理和协调,以确保数据的一致性和完整性。Spring Cloud Alibaba Seata提供了全局事务管理功能,可以跨多个服务进行事务管理。 2. 分布式事务的隔离性:在分布式系统,需要确保不同服务之间的事务操作是独立的,互相之间没有影响。Spring Cloud Alibaba Seata提供了分布式事务的隔离性功能,可以确保不同服务之间的事务操作是独立的。 3. 并发控制:在分布式系统,由于多个服务之间存在依赖关系,因此可能会出现并发冲突的情况。Spring Cloud Alibaba Seata提供了并发控制功能,可以确保多个服务之间的并发操作不会冲突。 4. 事务回滚:在分布式系统,如果某个服务的事务操作失败,需要对整个事务进行回滚。Spring Cloud Alibaba Seata提供了事务回滚功能,可以确保在分布式环境下的事务回滚操作是可靠的。 5. 可靠性:在分布式系统,需要确保事务操作是可靠的,不会出现数据丢失或者数据不一致的情况。Spring Cloud Alibaba Seata提供了高可靠性的事务管理功能,可以确保分布式事务操作的可靠性和安全性。 综上所述,Spring Cloud Alibaba Seata是一款非常强大的分布式事务解决方案,可以帮助开发人员在分布式环境下处理复杂的事务操作,确保数据的一致性和完整性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值