springcloud+eureka+seata实现分布式事务处理

Seata分布式事务


一、为什么要用Seata

1.问题背景

在微服务的架构下,数据不一致

2.数据不一致的原因

在微服务的环境下,由于调用链路跨越多个应用,甚至跨越多个数据源,数据的一致性在普通情况下难以保证,导致数据不一致的原因非常多,这里列举了三个最常见的原因

1.业务异常一个服务链路调用中,如果调用的过程出现业务异常,产生异常的应用独立回滚,非异常的应用数据已经持久化到数据库。
2.网络异常调用的过程中,由于网络不稳定,导致链路中断,部分应用业务执行完成,部分应用业务未被执行。
3.服务不可用若服务不可用,无法被正常调用,也会导致问题的产生

在以往如果出现数据不一致的问题,相信大多数的解决方案是这样的

1.人工补偿数据
2.定时任务检查和补偿数据
但是这两种方式的缺点也是显然意见的,一种是浪费大量的人力成本和时间,另外一种是浪费大量的系统资源去检查数据是否一致和额外的人力成本。

二、Seata简介

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

Seata原理和设计

我们可以把一个分布式事务理解成一个包含了若干分支事务的全局事务,全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个满足ACID的本地事务。这是我们对分布式事务结构的基本认识,与 XA 是一致的。
在这里插入图片描述

协议分布式事务处理过程的三个组件

  1. Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
  2. Transaction Manager ™: 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
  3. Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
    在这里插入图片描述

一个典型的分布式事务过程

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;

  2. XID 在微服务调用链路的上下文中传播; RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖; TM 向 TC 发起针对

  3. XID 的全局提交或回滚决议; TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
    在这里插入图片描述

Seata可靠吗

一项新的技术出来不是可以直接运用,需要市场的检验,观察他是否被别的企业运用到生产环境,是否真的可靠
图片不是很全,可以去seata官网查看更多合作企业图片不是很全,可以去seata官网查看更多合作企业
Seata官网

可以看出很多知名企业都使用Seata,让我们一起来看看seata在springcloud项目中是如何使用的。

三、springcloud+eureka+seata实现分布式事务处理


演示项目组成结构

在这里插入图片描述

搭建步骤

配置Seata-server

搭建Eureka

Spring Cloud教程 | 第一篇:服务的注册与发现 | Eureka
可以参照上述文章进行配置Eureka,然后对application.yml进行端口号修改
在这里插入图片描述
我们更改下端口号为6001
在这里插入图片描述

创建seata-server数据库
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- 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 = 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(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;

在这里插入图片描述

配置seata-server

从官网下载seata-server,这里下载的是seata-server-1.4.2.zip,下载地址:https://github.com/seata/seata/releases
解压seata-server安装包到指定目录,接着我们修改conf目录下的file.conf
配置文件,主要修改自定义事务组名称,事务日志存储模式为db及数据库连接信息;
把mode形式改为db:
把mode形式改为db
之后在file.conf设置数据库等信息,主要设置数据库链接,账户名和密码

## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.cj.jdbc.Driver"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "jdbc:mysql://127.0.0.1:3306/seata-server?serverTimezone=UTC"
    user = "root"
    password = "123456"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

然后进入conf下面的registry.conf文件,修改注册中心为eureka如图:
在这里插入图片描述
现在我们启动eureka-server
在这里插入图片描述
并启动seata-server:
双击\seata-server-1.4.2\bin\seata-server.bat
在这里插入图片描述
我们访问Eureka服务http://localhost:6001
能看到seata-server已经注册到eureka:
在这里插入图片描述

配置搭建分布式事务

其实就是不同的Client,具体如何搭建可参考Spring Cloud教程 | 第一篇:服务的注册与发现 | Eureka:四、创建Client
Maven依赖
pom.xml添加如下:

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-seata</artifactId>
</dependency>

application.properties
配置文件中添加如下

logging.level.io.seata=info
spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group

在了解怎么配置之后
我们下载并打开演示用的例子https://gitee.com/lin_qichun/seata.git

springCloud-eureka-seata文件
并修改account,order,storage,bussiness里application.properties文件,file.conf,registry.conf文件
在这里插入图片描述

application.properties文件:

spring.application.name=storage-service
server.port=8081
spring.datasource.url=jdbc:mysql://rm-2zetd9474ydd1g5955o.mysql.rds.aliyuncs.com:3306/fescar?useSSL=false&serverTimezone=UTC
spring.datasource.username=workshop
spring.datasource.password=Workshop123
spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
logging.level.org.springframework.cloud.alibaba.seata.web=debug
logging.level.io.seata=info
eureka.instance.hostname=127.0.0.1
eureka.instance.prefer-ip-address=true
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:6001/eureka/
feign.hystrix.enabled=true
spring.main.allow-bean-definition-overriding=true

以及file.conf文件,service里边配置的事务组应该跟服务名保持一致
在这里插入图片描述

修改file.conf文件中greouplist为seata-server服务器地址,如图:
在这里插入图片描述
修改registry.conf
在这里插入图片描述
在BusinessService里需要使用分布事务处理的地方加上 @GlobalTransactional注解,如:
在这里插入图片描述

实例演示

启动account,order,storage,bussiness这四个项目

在这里插入图片描述在这里插入图片描述

在Navicat里连接远程数据库:
rm-2zetd9474ydd1g5955o.mysql.rds.aliyuncs.com
用户:workshop
密码:Workshop123
在这里插入图片描述

打开fescar
在这里插入图片描述
我们分别点开前三个表,可以观察到:
账户表U100000用户有10000
在这里插入图片描述
订单表则没有数据
在这里插入图片描述
库存表库存为100
在这里插入图片描述
我们使用postman发送一条请求http://localhost:8084/purchase/commit

在这里插入图片描述
得到全局事务提交反馈则表明成功
在这里插入图片描述

我们再去观察三个表:
账户表:钱少了3000
在这里插入图片描述
订单表:新增了一条记录
在这里插入图片描述
库存表:少了30库存
在这里插入图片描述
这时我们把库存改为比30小,或者钱数改为比3000小来模仿库存不足或者钱数不足的情况,我们这里用修改钱数举例
在这里插入图片描述

递交数据请求:
在这里插入图片描述
可看到执行回滚,观察3个表,数据都无改变

现在来模拟程序出错,看seata是否执行回滚

我们在AccountService里加上两行错误代码,账户程序在执行到此处时会报错,重启AccountApplication,同时我们将数据库里的钱修改大于3000,调用链接
在这里插入图片描述
在这里插入图片描述
可以看到报错,此时我们去观察数据库那3个表,发现数据没变,表明回滚成功


  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值