SEATA 配置

使用 nacos 做为配置中心配置 SEATA

当前 SEATA 版本: 1.4.1

TC (Transaction Coordinator) - 事务协调者

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

配置参数

参数文档

nacos bash 脚本

参考 SEATA github 配置说明

同步 config 配置到 nacos

进入 TC 服务器

  • 新建文件夹 seata-config

  • 进入 seata-config

  • 新建 config.txt 文件并复制配置参数到 config.txt 文件中

  • 新建 nacos-config.sh 文件,同时复制 nacos bash 脚本到 nacos-config.sh 中

  • 使用以下命令同步配置参数到 nacos

    bash nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 3a2aea46-07c6-4e21-9a1e-8946cde9e2b3 -u nacos -w nacos
    

    得到输出

    set nacosAddr=127.0.0.1:8848
    set group=SEATA_GROUP
    Set transport.type=TCP successfully 
    Set transport.server=NIO successfully 
    .
    .
    .
    =========================================================================
    Complete initialization parameters,  total-count:80 ,  failure-count:0 
    =========================================================================
    Init nacos config finished, please start seata-server. 
    

使用 docker 部署 SEATA

Docker 部署 SEATA 官方文档

进入 TC 服务器,并进入 seat-config 文件夹

  • 新建 registry.conf 文件,并添加以下内容,registry 配置参考

  • 新建 file.conf 文件并添加以下内容(可选,可通过 nacos 读取)

  • 运行 docker 命令

    • 注意: 当在 config.txt 中配置 store.mode=db 时,需要在配置的数据库连接中初始化表 global_tablebranch_tablelock_tablesql 传送门
    docker run -d --name seata-server \
          --net=host \
          -p 8091:8091 \
          -e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
          -v /root/seata-config:/root/seata-config  \
          seataio/seata-server:1.4.1
    

    挂载目录为 TC 服务器配置目录。

TM (Transaction Manager) - 事务管理器

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

例子:业务聚合服务

  • SEATA 包引入。pom 配置如下,当使用 spring-cloud-starter-openfeign 包时,需要移除 spring-cloud-starter-openfeign 包,spring-cloud-starter-alibaba-seata 中已经包含了 spring-cloud-starter-openfeign ,再次引入可能导致包冲突。

  • 添加 registry.conf。在工程中 resource 目录下添加如下内容

  • 配置 bootstrap.properties,添加配置,内容如下,seata.tx-service-groupnamespace 改为对应的值

在 TM 中通过 @GlobalTransactional 开启全局异常,示例代码:

 
    @GlobalTransactional
    @GetMapping({"create"})
    public String create(String name,Integer age) {
        ...
        return "创建成功";
    }

RM (Resource Manager) - 资源管理器

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

例子:被调用服务。

  • 配置 bootstrap.properties,添加配置,内容如下,seata.tx-service-groupnamespace 改为对应的值

  • 对需要做回滚的业务标记 @Transactional(rollbackFor = Exception.class)

  • 如果配置了全局异常处理,使用 SEATA API 发起事务回滚

      @ExceptionHandler(value = Exception.class)
      @ResponseBody
      public String exceptionHandler(Exception e) {
          ...
          try {
              String xid = RootContext.getXID();
              if (StringUtils.isNotEmpty(xid)) {
                  GlobalTransactionContext.reload(RootContext.getXID()).rollback();
              }
          } catch (TransactionException transactionException) {
              transactionException.printStackTrace();
              log.error("===TransactionException==={}", transactionException.getMessage());
          }
          return e.getMessage();
      }
    

    或者通过 AOP 全局处理回滚

    
    /**
    * @author Zhang_Xiang
    * @since 2021/2/22 17:36:16
    */
    @Aspect
    @Component
    @Slf4j
    public class TxAspect {
    
        @Pointcut("execution(public * *(..))")
        public void publicMethod() {
        }
    
        @Pointcut("within(com.*.service.impl..*)")
        private void services() {
        }
    
        @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
        private void transactional() {
        }
    
        @Pointcut("within(com.*.webapi.controller..*)")
        private void actions() {
        }
    
        @Pointcut("@annotation(org.springframework.web.bind.annotation.ExceptionHandler))")
        private void validatedException() {
        }
    
        @Before(value = "validatedException()")
        public void beforeValidate(JoinPoint joinPoint) throws TransactionException {
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                return;
            }
            Exception e = (Exception) args[0];
            if (e instanceof MethodArgumentNotValidException || e instanceof BindException || e instanceof
                    ConstraintViolationException) {
                globalRollback();
            }
        }
    
        @AfterThrowing(throwing = "e", pointcut = "publicMethod()&&services()&&transactional()")
        public void doRecoveryMethods(Throwable e) throws TransactionException {
            log.info("===method throw===:{}", e.getMessage());
            globalRollback();
        }
    
        @AfterReturning(value = "publicMethod()&&actions()", returning = "result")
        public void afterReturning(RestResponse<?> result) throws TransactionException {
            log.info("===method finished===:{}", result);
            if (result.isFail()) {
                globalRollback();
            }
        }
    
        //region private methods
    
        private void globalRollback() throws TransactionException {
            if (!StringUtils.isBlank(RootContext.getXID())) {
                log.info("===xid===:{}", RootContext.getXID());
                GlobalTransactionContext.reload(RootContext.getXID()).rollback();
            }
        }
    
        //endregion
    }
    
  • AT(Automatic Transaction) 模式下配置 undo_log 数据库表,mysql 建表 sql 如下

    CREATE TABLE IF NOT EXISTS `undo_log`
    (
        `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(6)  NOT NULL COMMENT 'create datetime',
        `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
      AUTO_INCREMENT = 1
      DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
    

关闭 SEATA

seata.enabled=false
02-23 17:28