Seata安装与使用以及部分常见问题(保姆级教程)

一、Seata是什么?

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

1.相关事务概念

  • 全局事务:全局事务指的是一次性操作多个资源管理器完成的事务,由一组分支事务(本地事务)组成。

  • 分支事务(本地事务):本地事务由本地资源管理器(通常指数据库管理系统 DBMS,例如 MySQL、Oracle 等)管理,严格地支持 ACID 特性,高效可靠。本地事务不具备分布式事务的处理能力,隔离的最小单位受限于资源管理器,即本地事务只能对自己数据库的操作进行控制,对于其他数据库的操作则无能为力。

工作流程

Seata 对分布式事务的协调和控制,主要是通过 XID 和 3 个核心组件实现的。

XID

  • XID 是全局事务的唯一标识,它可以在服务的调用链路中传递,绑定到服务的事务上下文中。(很重要,用于判断是否是一个事务)

核心组件

  • Seata的核心组件可分为Seata服务端和Seata客户端两类

Seata 定义了 3 个核心组件:

  • TC(Transaction Coordinator):事务协调器,直接调度事务参与者RM。负责将RM的反馈结果响应给TM,并听从TM的最终决议,将具体决议(提交或回滚)发送给RM执行。相当于中间人,主要负责维护全局事务和分支事务的状态。

  • TM(Transaction Manager):事务管理器,它是事务的发起者(具体的微服务)。根据RM第一阶段的执行结果,进行决议。并将决议反馈给TC。相当于发号施令的

  • RM(Resource Manager):资源管理器,其实就是事务的参与者。获取TC的执行命令具去执行分支事务的第一阶段以及第二阶段,并将执行结果反馈给TC,相当于具体做事的

以上三个组件相互协作,TC 以 Seata 服务器(Server)形式独立部署,TM 和 RM 则是以 Seata Client 的形式集成在微服务中运行。
在这里插入图片描述
Seata 的整体工作流程如下:

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功后,TC 会针对这个全局事务生成一个全局唯一的 XID(此时,由TM发起的全局事务已经开启)
  2. XID 通过服务的调用链传递到其他服务
  3. RM 向 TC 注册一个分支事务,并将其纳入 XID 对应全局事务的管辖(事务参与者执行本地事务,此时分支事务已经执行完成,并反馈给TC执行结果。可以理解为AT模式下的第一个阶段)
  4. TM 根据 TC 收集的各个分支事务的执行结果,向 TC 发起全局事务提交或回滚决议(事务协调者根据事务管理者的决议,发送提交或回滚的调度命令,可以理解为AT模式下的第二阶段)
  5. TC 调度 XID 下管辖的所有分支事务完成提交或回滚操作

二、seata安装和部署

0.前提环境

注:本文使用Mysql为例

需要使用Nacos 教程:https://gaohuanjie.blog.csdn.net/article/details/135294828

需要git Bash窗口
官方下载地址: https://www.git-scm.com/download/

git bash下载安装就行,如果不会提供一个教程 https://blog.csdn.net/qq_33204709/article/details/133963278

1.下载seata

官网下载地址:https://seata.io/zh-cn/blog/download.html
在这里插入图片描述

下载完成后,将文件解压到任意地方 (不要忘记解压的地方)

接下来的大部分操作都在下图的根目录展开

在这里插入图片描述

2.创建seata数据库

新建数据库seata,然后在解压的seata文件找到script->server->db->mysql.sql,执行这个sql脚本。
在这里插入图片描述

3.修改config.txt文件

config.txt文件在seata/script/config-center目录下,需要修改的地方如下:

在这里插入图片描述

4.seata服务注册到Nacos

1)创建命名空间

首先在nacos控制台添加新的命名空间

新建命名空间

空间ID可以不填,但是自动生成之后需要记住,接下来配置需要很重要

命名空间和描述可以任意但是最好写seata见名知意
在这里插入图片描述

1)将config.txt信息注册到Nacos

进入seata目录,找到nacos-config.sh,路径为:script->config-center->nacos->nacos-config.sh

进入git bash窗口,执行以下命令:

sh nacos-config.sh -h nacos服务地址 -p 8848 -g SEATA_GROUP -t 刚刚注册时候的命名空间id -u nacos -w nacos

案例
sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t cc7b3c50-d6e9-4b86-934a-1cd7e5a6a0e2 -u nacos -w nacos

参数详解:

  • -h nacos服务IP
  • -p nacos服务端口
  • -u nacos登录名
  • -w nacos登录密码
  • -g nacos 配置的分组名称,默认设置SEATA_GROUP 修改了后续的文件的group设置也要改
  • -t 上一步配置的命名空间ID

注意:-u 和 -w 后面是Nacos的登录账户和密码,最好不要修改,除非你能记住,修改后下面配置文件的nacos账号密码就要修改为对应的

执行完上述命令之后 配置数应该为103,可能不一样和(图为107是因为后续需要根据你的微服务数量添加对应的配置,后续说明)
在这里插入图片描述

5.修改seata配置文件

进入seata/conf目录下,有两个配置文件,把application.yml 随意修改一个名字,然后把 application.example.yml修改成application.yml 作为主要配置文件。

修改application.yml文件,这里使用的nacos作为注册中心,所以需要修改的地方有:

第一步将原来application.yml 文件中的console和security拷贝走,复制到主要配置文件(最初的application.example.yml)文件中

在这里插入图片描述
console放在这个位置
在这里插入图片描述

security放在最下面,注意缩进格式
在这里插入图片描述

然后开始修改主要配置文件(最初的application.example.yml)中的设置

首先修改seata
在这里插入图片描述
修改seata下的registry
在这里插入图片描述
修改seata下的store
在这里插入图片描述
至此文件就修改完成了

6.启动服务

找到seata-server.bat,点击启动,路径为:seata->bin->seata-server.bat,成功后可以在nacos控制台服务列表中看到多了一个服务。

如果闪退了说明有错误,需要在该目录下打开cmd控制台输入seata-server.bat 启动,错误是什么

常见问题

  • java不是命令,java环境的问题,配置环境即可解决
  • javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate),数据库的url链接没有加useSSL=false或者数据库是8.0的但是驱动没有加cj
  • xxx.security 该问题是复制的security缩进不对,没有被识别

部署成功后就会有seata-server的服务名的服务(其他服务是分布式服务,后续说明)

在这里插入图片描述


三、整合Nacos+Fegin+springCloud项目

注意所有的服务模块都需要该操作,在此只演示一个,但是所以服务都需要这些操作

1.添加seata依赖

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

2.设置application.properties添加

1)创建Nacos配置

第一步 在该页面创建配置
在这里插入图片描述

信息设置为这样,Data ID格式为service.vgroupMapping.XXX XXX可以任意起名,但是之后需要和properties中seata.tx-service-group和seata.service.vgroup-mapping.XXX 保持一致

Group改为SEATA_GROUP (Group是上文中设置为SEATA_GROUP的)

配置内容设置为Default

在这里插入图片描述

2)修改服务模块中的properties

seata.enabled=true
# 服务id,不重复即可
seata.application-id=wallet
seata.enable-auto-data-source-proxy=false

# group名,需要跟Nacos添加的配置保持一致
seata.tx-service-group=service-wallet-group
# seata.service.vgroup-mapping.XXX XXX需要跟上面保持一致
seata.service.vgroup-mapping.service-wallet-group=default
seata.service.disable-global-transaction=false
# seata.registry的信息,跟application.yml的registry信息保持一致
seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.namespace=cc7b3c50-d6e9-4b86-934a-1cd7e5a6a0e2
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
seata.registry.nacos.cluster=default
# seata.config的信息,跟application.yml的config信息保持一致
seata.config.type=nacos
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.namespace=cc7b3c50-d6e9-4b86-934a-1cd7e5a6a0e2
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos

seata.client.rm.report-success-enable=true
seata.client.rm.report-retry-count=5

3.在需要事务的方法上添加注解

该步不需要每个都配置,只需要在主要的方法上添加开启事务即可

// name任意,不重复即可  rollbackFor最好设置为Exception.class,不然有些异常不回滚
@GlobalTransactional(name = "order",rollbackFor = Exception.class)

// 案例
/**
    *
    * @param userId  用户id 目前只有1
    * @param productId 商品id 目前有1-4
    * @param quantity 购买数量
    * @param addressId 地址信息id
    * @return
    */
   @GetMapping("/order/add.do")
   @GlobalTransactional(name = "order",rollbackFor = Exception.class)
   public Map<String,Object> add(Long userId, Long productId, Integer quantity, Long addressId) {
       System.out.println("事务id是"+ RootContext.getXID());
       
       Order order = new Order();

       order.setUserId(userId);

       //通过productId获取商品信息
       Product product = IProductService.get(productId);
       order.setProductId(product.getId());
       order.setProductName(product.getName());//商品名称在后期可能发生变化,这里记录的是下单时商品名称
       order.setProductPrice(product.getPrice());//商品名称在后期可能发生变化,这里记录的是下单时商品名称

       //通过addressId获取地址信息

       Address address = IAddressService.get(addressId);
       order.setRealName(address.getRealName());//收件人姓名在后期可能发生变化,这里记录的是下单时用户选中的邮寄地址对应的收件人姓名
       order.setMobile(address.getMobile());//收件人手机号在后期可能发生变化,这里记录的是下单时用户选中的邮寄地址对应的收件人手机号
       order.setAddressDetail(address.getDetail());//收件人详细地址在后期可能发生变化,这里记录的是下单时用户选中的邮寄地址对应的收件人详细地址

       order.setQuantity(quantity);

       double amount = product.getPrice() * quantity;//计算商品总额

       Map<String,Object> map = new HashMap<>();
       if(IProductService.update(addressId,quantity) //修改商品库存
               && IWalletService.getWallet(userId,amount) //修改钱包余额
               && orderService.save(order)){//添加订单信息
           map.put("state","200");
           map.put("message","订单创建成功!");
       }else{
           map.put("state","100");
           map.put("message","订单创建失败!");

       }
       return map;

4.在启动类上添加启动注解

添加@EnableAutoDataSourceProxy

// 案例
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
@EnableAutoDataSourceProxy
public class OrderApplication {

public static void main(String[] args) {
    SpringApplication.run(OrderApplication.class, args);
}

至此就已经将seata事务管理添加进入项目了

5.结果展示

添加@GlobalTransactional注解日志打印,显示已经回滚
在这里插入图片描述
正常执行的子服务
在这里插入图片描述
异常的子服务
在这里插入图片描述

6.常见问题

xxx.undo_log不存在

java.sql.SQLSyntaxErrorException: Table ‘mall.undo_log’ doesn’t exist
在这里插入图片描述
在服务操作的目标数据库执行下SQL添加表

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(0) NOT NULL,
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(0) NOT NULL,
  `log_created` datetime(0) NOT NULL,
  `log_modified` datetime(0) NOT NULL,
  `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

事务不回滚

在添加@GlobalTransactional注解的服务中,日志显示status:RollBacked但是在其他服务模块中日志打印的是Commited

该问题一般是因为XID不一致导致的,一般不会出现该问题本文没有遇到该问题

提供一个方法来判断是否是XID不一致或者子服务中XID为null

// RootContext.getXID()就是获取事务的XID 在子服务和总服务下输出一下就能知道是否一致
System.out.println("事务id是"+ RootContext.getXID());

第一种情况
如果使用的不是本文的pring-cloud-starter-alibaba-seata这个依赖,而是使用的这以下依赖有可能出现该问题

<!--seata1.7.0-->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.7.0</version>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.7.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <version>2022.0.0.0-RC2</version>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
        </exclusion>
    </exclusions>
</dependency>

第二种情况
seata 分布式事务没有传递xid导致事务失效

本文没有出现这种情况,是第一种情况所以在此提供的一种解决方案

https://blog.csdn.net/qq_45278455/article/details/124364505

参考

安装和部署参考了该文章,并在此基础上改进

https://blog.csdn.net/m0_54239478/article/details/132553798

Seata 是一个开源的分布式事务解决方案,可以在 Docker 环境中进行安装。下面是安装 Seata 的步骤: 1. 首先,确保你已经安装了 Docker 和 Docker Compose。 2. 创建一个文件夹,用于存放 Seata 的配置文件和脚本。在该文件夹中创建一个名为 `docker-compose.yml` 的文件,并将以下内容复制到该文件中: ```yaml version: '3' services: seata-server: image: seataio/seata-server ports: - "8091:8091" environment: - SEATA_PORT=8091 volumes: - ./config:/seata-server/config ``` 3. 在同一文件夹中创建一个名为 `registry.conf` 的文件,并复制以下内容到该文件中: ```ini registry { type = "file" file { name = "file.conf" } } ``` 4. 在同一文件夹中创建一个名为 `file.conf` 的文件,并复制以下内容到该文件中: ```ini store { mode = "file" file { dir = "sessionStore" } } ``` 5. 创建一个名为 `sessionStore` 的文件夹,用于存储 Seata 的会话数据。 6. 打开终端或命令提示符,进入到包含以上文件的文件夹中。 7. 运行以下命令启动 Seata 容器: ``` docker-compose up -d ``` 8. 等待一段时间,直到容器启动完成。你可以使用以下命令查看容器的状态: ``` docker ps ``` 9. 如果一切顺利,你应该能够看到名为 `seata-server` 的容器正在运行。 至此,你已经成功地在 Docker 环境中安装Seata。你可以通过 `http://localhost:8091` 访问 Seata 控制台来管理和监控你的分布式事务。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值