Seata 事务,SpringCloudAlibaba+Nacos

版本信息

组件版本说明
SpringBoot2.1.3.RELEASE
SpringCloudGreenwich.SR6
SpringCloudAlibaba2.1.4.RELEASE
Nacos1.4.1
Seata1.4.0

部署Seata服务端(https://github.com/seata/seata/releases)

  • 下载Seata服务端 https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.tar.gz
  • 解压tar包,进入conf目录,编辑== registry.conf ==(Seata配置注册中心和属性信息的文件)文件。使用nacos注册中心,配置中心暂时使用file,测试正常以后换为nacos

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

  nacos {
    application = "seata-server"
    serverAddr = "域名|IP:端口"
    group = "SEATA_GROUP"
    namespace = "dev"
    cluster = "default"
    username = ""
    password = ""
  }
}

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

  nacos {
    serverAddr = "域名|IP:端口"
    namespace = "dev"
    group = "SEATA_GROUP"
    username = ""
    password = ""
    dataId = "seataServer.properties"
  }
  file {
    name = "file.conf"
  }

}
  • 进入bin目录,启动Seata服务端
# -h 注册到nacos后的ip
# -p 服务端口
# -m 启动模式(暂时使用文件模式),运行正常后改为数据库模式
./seata-server.sh -h 102.47.15.128 -p 9000 -m file -n 1

在这里插入图片描述

  • 修改配置中心为Nacos。
    • 下载https://github.com/seata/seata/archive/refs/tags/v1.4.2.zip 源码
    • 复制seata-1.4.2\script\config-center\config.txt文件到服务器seata-server-1.4.2目录下
    • 复制seata-1.4.2\script\config-center\nacos\nacos-config.sh文件到seata-server-1.4.2\conf目录下
    • 修改config.txt文件,修改存储方式(store.mode),也可同步到nacos上以后再去改。(启动seata-server时指定 -m db 为使用db存储模式,或者修改config中store.mode=file为store.mode=db,或同步到nacos以后再nacos中修改)
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=username
store.db.password=password

    • 同步配置信息到nacos
#-h nacos域名|IP 
#-p nacos端口 
#-g 配置信息分组 
#-t 配置信息命名空间
./nacos-config.sh -h 123.123.123.123 -p 80 -g SEATA_GROUP -t dev

在这里插入图片描述

    • 重新启动seata(如果-m db 模式时一定要修改数据库信息),seata数据库sql如下:
-- -------------------------------- 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;

springboot 整合seata

  • maven依赖
<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <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>${seata.version}</version>
        </dependency>
  • seata配置
seata:
  tx-service-group: ${spring.application.name}
  application-id: ${spring.application.name}
  registry:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.config.server-addr}
      namespace: ${spring.cloud.nacos.config.namespace}
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: ${spring.cloud.nacos.config.server-addr}
      namespace: ${spring.cloud.nacos.config.namespace}
      group: SEATA_GROUP

注意:seata.tx-service-group 对应的值一定要再nacos中配置以后才可以使用。如seata.tx-service-group=user;配置如下
在这里插入图片描述

  • 使用AT代理数据库
  @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Primary
    @Bean
    public DataSourceProxy dataSource(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
  • 全局事务入口添加注解:@GlobalTransactional(回开启全局事务,注册全局事务XID,并由SeataHandlerInterceptor类将XID设置再http请求头中传递),即可实现分布式事务
    注意:分布式事务链路中的个个分支事务异常得向上抛,才能被事务发起者感知到,然后触发回滚。或自己根据业务判断是否异常,主动触发回滚(分支事务也可主动触发回滚)。如下:
String xid = RootContext.getXID();
            if(StringUtils.isNotBlank(xid)){
                GlobalTransactionContext.reload(xid).rollback();
            }

因为我的项目中都是 Controler接口互相调用,也设置了全局异常处理,所以不会自动异常回滚。处理方式为在异常处理中主动触发回滚。如下:

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理其他异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultVo exceptionHandler(HttpServletRequest req, Exception e){
        try{
        	//主动回滚
            String xid = RootContext.getXID();
            if(StringUtils.isNotBlank(xid)){
                GlobalTransactionContext.reload(xid).rollback();
            }
        }catch (Exception exception){
            exception.printStackTrace();
        }
        String msg = e.getMessage();
        if(e instanceof NullPointerException){
            msg = "NULL_POINTER_EXCEPTION";
        }
        e.printStackTrace();
        return ResultVo.fail(msg );
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值