分布式事务

在 Dubbo 中使用分布式事务

如何使用

第一步

首次访问: https://seata.apache.org/unversioned/download/seata-server

下载我们需要使用的 seata1.5.2 服务

第二步

  1. 将 undo_log 表添加到参与全局事务的数据库中(TCC、SAGA、XA 可以跳过此步骤)
-- for AT mode you must to init this sql for you business database. the seata server not need it.
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';
  1. 在您的 mysql 数据库中创建一个名为 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(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 = utf8;

第三步

将 seata 依赖项引入您的项目

spring-boot 应用程序

            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>1.5.2</version>
            </dependency>

spring 应用程序

            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-all</artifactId>
                <version>1.5.2</version>
            </dependency>

第四步

spring-boot 应用程序

参考 seata/script/client/spring at develop seata/seata (github.com)

将其添加到您的项目的 application.yml 中。

seata:
  enabled: true
  application-id: applicationName
  tx-service-group: my_test_tx_group
  enable-auto-data-source-proxy: true #Only AT and XA modes need to be true, and the data source will be automatically proxied after opening
  data-source-proxy-mode: AT #Optional AT&XA
  config:
    type: nacos
    nacos:
      #namespace: If the configuration is created in a non-default namespace, please fill in the id of the namespace here
      serverAddr: 127.0.0.1:8848
      group: SEATA_GROUP
      username: "nacos"
      password: "nacos"
      data-id: seata.properties
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      #namespace: If the configuration is created in a non-default namespace, please fill in the id of the namespace here
      username: "nacos"
      password: "nacos"

spring 应用程序

添加 seata/script/client/conf at develop · seata/seata (github.com) 在 registry.conf 下,因为高可用性部署使用第三方配置中心,所以不需要 file.conf

registry {
  # file, nacos, eureka, redis, zk, consul, etcd3, sofa, custom
  type = "nacos"
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    username = ""
    password = ""
    ##if use MSE Nacos with auth, mutex with username/password attribute
    #accessKey = ""
    #secretKey = ""
    ##if use Nacos naming meta-data for SLB service registry, specify nacos address pattern rules here
    #slbPattern = ""
  }
}

config {
  # file, nacos, apollo, zk, consul, etcd3, springCloudConfig, custom
  type = "nacos"
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
    ##if use MSE Nacos with auth, mutex with username/password attribute
    #accessKey = ""
    #secretKey = ""
    dataId = "seata.properties"
  }
}

第五步

运行您下载的 nacos,并参考 https://github.com/seata/seata/tree/develop/script/config-center 并修改 config.txt

#Only used by client
#The transaction group is called my_test_tx_group and the corresponding seata-server cluster is default
service.vgroupMapping.my_test_tx_group=default
#The following are only used by server
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
store.db.user=username
store.db.password=password
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

打开 nacos 控制台,创建一个数据Id 为 seata.properties 的配置,在对应的命名空间下,填写 group 为 SEATA_GROUP,填写上面的内容,选择类型为 properties 保存 Dingtalk_20220724021635.jpg.png

第六步

更改服务器中的 application.yml

server:
  port: 7091

spring:
  application:
    name: seata-server

record:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata

console:
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      #namespace: If the configuration is created in a non-default namespace, please fill in the id of the namespace here
      group: SEATA_GROUP
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
      data-id: seata.properties
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
      cluster: default
      #namespace: If the configuration is created in a non-default namespace, please fill in the id of the namespace here
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*. png, /**/*.ico, /console-fe/public/**, /api/v1/auth/login

第七步

以下是一个在全局事务调用者(启动全局事务的服务)的接口中添加 @GlobalTransactional 的示例

@GetMapping(value = "testCommit")
@GlobalTransactional
public Object testCommit(@RequestParam(name = "id",defaultValue = "1") Integer id,
    @RequestParam(name = "sum", defaultValue = "1") Integer sum) {
    Boolean ok = productService. reduceStock(id, sum);
    if (ok) {
        LocalDateTime now = LocalDateTime.now();
        Orders orders = new Orders();
        orders.setCreateTime(now);
        orders.setProductId(id);
        orders. setReplaceTime(now);
        orders. setSum(sum);
        orderService. save(orders);
        return "ok";
    } else {
        return "fail";
    }
}

Spring 应用程序在使用 AT 或 XA 模式时,需要手动代理数据源以选择事务模式并初始化事务扫描器

@Primary
@Bean("dataSource")
public DataSource dataSource(DataSource druidDataSource) {
    //AT agent choose one of the two
    return new DataSourceProxy(druidDataSource);
    //XA Proxy
    return new DataSourceProxyXA(druidDataSource)
}
       @Bean
       public GlobalTransactionScanner globalTransactionScanner() {
           return new GlobalTransactionScanner("application name", "my_test_tx_group");
       }

如果您使用 tcc 模式,则需要在相应提供者的 serviceimpl 中额外定义两阶段 try 和 confirm(commit) cancel(rollback)

spring-boot 应用程序需要关闭数据源代理

seata:
  enable-auto-data-source-proxy: false
/**
 * Define two-phase commit name = the bean name of the tcc, globally unique commitMethod = commit is the two-phase confirmation method rollbackMethod = rollback is the two-phase cancellation method
 * useTCCFence=true is to enable anti-hanging
 * BusinessActionContextParameter annotation to pass parameters to the second stage
 *
 * @param params - input parameters
 * @return String
 */
@TwoPhaseBusinessAction(name = "beanName", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)
public void insert(@BusinessActionContextParameter(paramName = "params") Map<String, String> params) {
    logger.info("You can reserve resources here, or use the characteristics of tcc to mix with AT. In the second stage, use the messages stored here in the first stage and send them out through the second stage, such as redis, mq and other operations");
}

/**
 * The confirmation method can be named otherwise, but it must be consistent with the commitMethod. The context can pass the parameters of the try method
 *
 * @param context context
 * @return boolean
 */
public void commit(BusinessActionContext context) {
    logger.info("Reserved resources are actually processed, or send mq messages and redis storage");
}

/**
 * Two-stage cancellation method
 *
 * @param context context
 * @return boolean
 */
public void rollback(BusinessActionContext context) {
    logger.info("Release reserved resources, or clear the message cache sent when the first phase is ready to be submitted by the second phase");
}

linux/macos

cd bin

sh seata-server.sh

windows

cd bin
./seata-server.bat

运行 seata-server,成功后,运行您自己的服务 dubbo provider&consumer

第八步是构建高可用的 Seata-server

由于 seata-server 支持计算和存储分离模式,并支持将服务地址暴露给多个注册中心,因此只需要按照第六步配置,然后进行水平扩展即可

详情请访问: https://seata.io/


上次修改时间:2024 年 2 月 1 日: 更新 seata 下载链接 (e9b9b1bb61e)