Spring-Cloud-Alibaba之Seata 1.4.2

742 阅读4分钟
当前环境:
spring-boot: 2.2.2.RELEASE
spring-cloud: Hoxton.SR1
spring-cloud-alibaba: 2.1.0.RELEASE
Nacos: 2.0.4
Seata: 1.4.2

1、下载安装Seata,修改Seata

官网: http://seata.io/
版本历史: https://github.com/seata/seata/releases

1.1、修改file.conf文件

修改存储方式为数据库存储mode = 'db' image.png

1.2、修改registry.conf文件

1.2.1、修改 registry 注册中心类型为 Nacos

registry {
    type = "nacos"
    nacos {
        application = "seata-server"  # 服务名称
        serverAddr = "127.0.0.1:8848"  # nacos地址
        group = "SEATA_GROUP"  # nacos分组
        namespace = "e9a5746d-b8bd-455b-912b-e26d60122c88"  # nacos名称空间id
        cluster = "default"
        username = "nacos"  # nacos用户名
        password = "nacos"  # nacos密码
      }
}

image.png

1.2.2、修改 config 配置中心类型为 Nacos

config {
  type = "nacos"
  nacos {
    serverAddr = "127.0.0.1:8848"  # nacos地址
    namespace = "e9a5746d-b8bd-455b-912b-e26d60122c88"  # nacos名称空间id
    group = "SEATA_GROUP"  # nacos分组
    username = "nacos"  # nacos用户名
    password = "nacos"  # nacos密码
    dataId = "seata-server.properties"  # nacos配置文件dataId
  }
}

image.png

2、在Nacos中编写配置文件

image.png 配置文件内容

transport.type=TCP

transport.server=NIO

transport.heartbeat=true

transport.enableClientBatchSendRequest=true

transport.threadFactory.bossThreadPrefix=NettyBoss

transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker

transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler

transport.threadFactory.shareBossWorker=false

transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector

transport.threadFactory.clientSelectorThreadSize=1

transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread

transport.threadFactory.bossThreadSize=1

transport.threadFactory.workerThreadSize=default

transport.shutdown.wait=3

service.vgroupMapping.my_tx_group=default #自定义,一定注意与spring cloud配置文件保持一致

service.default.grouplist=127.0.0.1:8091 #seata服务

service.enableDegrade=false

service.disableGlobalTransaction=false

client.rm.asyncCommitBufferLimit=10000

client.rm.lock.retryInterval=10

client.rm.lock.retryTimes=30

client.rm.lock.retryPolicyBranchRollbackOnConflict=true

client.rm.reportRetryCount=5

client.rm.tableMetaCheckEnable=false

client.rm.tableMetaCheckerInterval=60000

client.rm.sqlParserType=druid

client.rm.reportSuccessEnable=false

client.rm.sagaBranchRegisterEnable=false

client.rm.tccActionInterceptorOrder=-2147482648

client.tm.commitRetryCount=5

client.tm.rollbackRetryCount=5

client.tm.defaultGlobalTransactionTimeout=60000

client.tm.degradeCheck=false

client.tm.degradeCheckAllowTimes=10

client.tm.degradeCheckPeriod=2000

client.tm.interceptorOrder=-2147482648

store.mode=db

store.lock.mode=db

store.session.mode=db

store.publicKey=

store.file.dir=file_store/data

store.file.maxBranchSessionSize=16384

store.file.maxGlobalSessionSize=512

store.file.fileWriteBufferCacheSize=16384

store.file.flushDiskMode=async

store.file.sessionReloadReadSize=100

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&rewriteBatchedStatements=true

store.db.user=root

store.db.password=root

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

store.redis.mode=single

store.redis.single.host=127.0.0.1

store.redis.single.port=6379

store.redis.sentinel.masterName=

store.redis.sentinel.sentinelHosts=

store.redis.maxConn=10

store.redis.minConn=1

store.redis.maxTotal=100

store.redis.database=0

store.redis.password=

store.redis.queryLimit=100

server.recovery.committingRetryPeriod=1000

server.recovery.asynCommittingRetryPeriod=1000

server.recovery.rollbackingRetryPeriod=1000

server.recovery.timeoutRetryPeriod=1000

server.maxCommitRetryTimeout=-1

server.maxRollbackRetryTimeout=-1

server.rollbackRetryTimeoutUnlockEnable=false

server.distributedLockExpireTime=10000

client.undo.dataValidation=true

client.undo.logSerialization=jackson

client.undo.onlyCareUpdateColumns=true

server.undo.logSaveDays=7

server.undo.logDeletePeriod=86400000

client.undo.logTable=undo_log

client.undo.compress.enable=true

client.undo.compress.type=zip

client.undo.compress.threshold=64k

log.exceptionRate=100

transport.serialization=seata

transport.compressor=none

metrics.enabled=false

metrics.registryType=compact

metrics.exporterList=prometheus

metrics.exporterPrometheusPort=9898

下面这几项配置修改为自己项目的信息,service.vgroupMapping.my_tx_group 后续在spring cloud项目的配置文件中会用到,my_tx_group要和项目中保持一致:

  • service.vgroupMapping.my_tx_group=default
  • service.default.grouplist=127.0.0.1:8091
  • store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
  • store.db.user=root
  • store.db.password=root

3、创建seata相关的数据库和表

3.1、创建seata数据库和相关表

创建名为seata的数据库,并使用下面语句创建数据库表

image.png

-- -------------------------------- 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;

3.2、在每个业务数据库中创建 undo_log 表

该项目以订单、库存、账户三个数据库演示,分别创建 undo_log 表 image.png

CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) 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';

4、运行seata

4.1、先运行nacos

进入nacos/bin目录下执行:

Windows: startup.cmd
Mac: sh startup.sh

4.2、再运行seata

进入seata/bin目录下执行:

Windows: seata-server.bat
Mac: sh seata-server.sh

seata运行成功,并且在nacos中发现seata-server

image.png

image.png

5、spring-cloud 项目配置

5.1、pom.xml 引入 spring-cloud-starter-alibaba-seata

<!-- seata 指定版本为本地安装的1.4.2 -->
<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>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.4.2</version>
</dependency>

5.1、application.yml 配置

seata:
  # 开启自动装配
  enabled: true
  # 本客户端的微服务名称
  application-id: seata-order-service
  # 读取哪个事务分组
  # 例如此时会读取 SEATA_GROUP 这个分组下的 service.vgroupMapping.my_test_tx_group 这个属性的值。从上面的配置可以知道笔者此处的最终值为 testCluster。后面程序运行会找到 testCluster 这个集群的seata服务端,进行通讯。
  tx-service-group: my_tx_group
  # 配置中心设置
  config:
    type: nacos
    nacos:
      username: nacos
      password: nacos
      server-addr: localhost:8848
      group: SEATA_GROUP
      namespace: e9a5746d-b8bd-455b-912b-e26d60122c88
      data-id: seata-server.properties
  # 注册中心设置
  registry:
    type: nacos
    nacos:
      # SEATA服务中心的微服务名,此处与服务端保持一致
      application: seata-server
      server-addr: localhost:8848
      username: nacos
      password: nacos
      group: SEATA_GROUP
      namespace: e9a5746d-b8bd-455b-912b-e26d60122c88
  # 服务中心
  service:
    vgroup-mapping:
      my_tx_group: default

5.3、使用seata对数据源进行代理

seata 1.0 之后版本开启了数据源自动代理,无需创建DataSourceProxy

/**
 * 使用Seata对数据源进行代理
 */
@Configuration
public class DataSourceProxyConfig {

    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    /**
     * seata 0.9之后版本不需要使用 DataSourceProxy 代理数据源,会自动使用代理,直接使用DataSource即可
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }

}
// 主启动类中关闭数据源自动配置类,使用seata对数据源管理
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class SeataAccountMain {
    public static void main(String[] args) {
        SpringApplication.run(SeataAccountMain2003.class, args);
    }
}

5.4、开启分布式事务@GlobalTransactional

name:事务名称,可自定义,多个事务的情况下要保证唯一 rollbackFor:表示什么情况下回滚

@GlobalTransactional(name = "tx_create_order", rollbackFor = Exception.class)
public CommonResult createOrder(Order order) {
    // 业务逻辑....
}

注意事项:

下面引用到my_tx_group的地方,名字可以自定义,但必须保持一致;以及用到nacos配置项的地方,配置数据(dataId,名称空间id等)也要保持一致

image.png

image.png

image.png

image.png