seata 学习记录

2020-04-04

SEATA AT MODEL DOC

Simple Extensible Autonomous Transacation Architecture,seata是简单的、可扩展、自主性高的分布式架构

SEATA Server Configure

因我们使用正式的1.0.0-GA 版本,网上大多数找到的说明都是0.X版本,有不少变动,比如,在server中取消了db_store.sql的脚本,如找不到相关内容,可以通过源码来查找,比如db脚本源码:mysql db script

  1. 下载 seata-server

  2. 创建数据库(seata),可自定义,在file.conf中要用到。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    -- -------------------------------- 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,
    `gmt_modified` DATETIME,
    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;
  3. 编辑file.conf配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    service {
    #transaction service group mapping
    vgroup_mapping.sunrise_tx_group = "default"
    #only support when registry.type=file, please don't set multiple addresses
    default.grouplist = "127.0.0.1:8091"
    #disable seata
    disableGlobalTransaction = false
    #degrade current not support
    enableDegrade = false
    #disable
    disable = false
    #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
    max.commit.retry.timeout = "-1"
    max.rollback.retry.timeout = "-1"
    }

    ## transaction log store, only used in seata-server
    store {
    ## store mode: file、db
    mode = "db"

    ## file store property
    file {
    ## store location dir
    dir = "sessionStore"
    #degrade current not support
    enableDegrade = false
    #disable
    disable = false
    #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
    max.commit.retry.timeout = "-1"
    max.rollback.retry.timeout = "-1"
    }

    ## transaction log store, only used in seata-server
    store {
    ## store mode: file、db
    mode = "db"

    ## file store property
    file {
    ## store location dir
    dir = "sessionStore"
    }

    ## database store property
    db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://172.16.28.177:3306/seata"
    user = "wr"
    password = "wr"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
    }
    }
  4. 编辑conf/regiester.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    registry {
    # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
    # 更改1
    type = "eureka"

    nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
    }
    eureka {
    # 更改2
    serviceUrl = "http://172.16.1.187:21001/eureka"
    application = "default"
    weight = "1"
    }
    redis {
    serverAddr = "localhost:6379"
    db = "0"
    }
    zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
    }
    consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
    }
    etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
    }
    sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
    }
    file {
    name = "file.conf"
    }
    }

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

    nacos {
    serverAddr = "localhost"
    namespace = ""
    }
    consul {
    serverAddr = "127.0.0.1:8500"
    }
    apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
    }
    zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
    }
    etcd3 {
    serverAddr = "http://localhost:2379"
    }
    file {
    name = "file.conf"
    }
    }

    本次实现,我们还没有添加configration的时候,因此只需要更改register块中的相关属性即可,在后续我们实现了nacos config之后,再同步更新这里。

  5. 启动服务端

    1
    [root@localhost.localdomain /usr/local/geekplus/server/seata]# bin/seata-server.sh

    eureka中看到如下8091端口的服务之后,即启动seata-server正常!
    eureka ha

Client Configure

AT Model

需要在每一个业务库中添加数据表undo_log.

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Broadway-Demo

使用Springboot项目有一个小规律,我叫它为使用三部曲。
broadway-ws-tally-service项目为例:

  • 第一步:加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!--seata-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-seata</artifactId>
    <version>2.1.0.RELEASE</version>
    <exclusions>
    <exclusion>
    <artifactId>seata-all</artifactId>
    <groupId>io.seata</groupId>
    </exclusion>
    </exclusions>
    </dependency>
    <dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.0.0</version>
    </dependency>
  • 第二步:开启注解

    1
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

    在这里需要说明一下,我们需要排除掉SpringBoot默认自动注入的DataSourceAutoConfigurationBean, 因为SEATA是基于数据源拦截来实现的分布式事务,因此,我们需要自定义数据源配置信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    package com.geekplus.broadway.ws.tally.application;

    import com.alibaba.druid.pool.DruidDataSource;
    import io.seata.rm.datasource.DataSourceProxy;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import javax.sql.DataSource;

    @Configuration
    public class DataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
    DruidDataSource druidDataSource = new DruidDataSource();
    return druidDataSource;
    }

    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSource(DataSource druidDataSource){
    return new DataSourceProxy(druidDataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSourceProxy);
    sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
    .getResources("classpath*:/com/geekplus/*.xml"));
    sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
    return sqlSessionFactoryBean.getObject();
    }
    }

    该配置类,一般放在与启动类相同的目录即可!

  • 第三部:改配置

    1
    2
    3
    4
    5
    spring:
    cloud:
    alibaba:
    seata:
    tx-service-group: sunrise_tx_group

    TIPS:
    tx-service-group 一定要server,client都要保持一致,否则能坑死你!
    tx-service-group 一定要server,client都要保持一致,否则能坑死你!
    tx-service-group 一定要server,client都要保持一致,否则能坑死你!

以上三步是大多数SpringBoot 项目的通用做法,当然我们的SEATA暂时还不是那么完善,配置信息目前还不支持在application.yml中配置完全,因此,我们需要2个核心的client 配置文件file.confregister.conf.

  • file.conf 该文件主要是用于连接RM的,我们只需要关注上面所说的tx-service-group就行,其他配置默认就可以。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    transport {
    # tcp udt unix-domain-socket
    type = "TCP"
    #NIO NATIVE
    server = "NIO"
    #enable heartbeat
    heartbeat = true
    # the client batch send request enable
    enable-client-batch-send-request = true
    #thread factory for netty
    thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
    }
    shutdown {
    # when destroy server, wait seconds
    wait = 3
    }
    serialization = "seata"
    compressor = "none"
    }

    service {
    #transaction service group mapping
    vgroup_mapping.sunrise_tx_group = "default"
    #only support when registry.type=file, please don't set multiple addresses
    default.grouplist = "127.0.0.1:8091"
    #disable seata
    disableGlobalTransaction = false
    }

    client {
    rm {
    async.commit.buffer.limit = 10000
    lock {
    retry.internal = 10
    retry.times = 30
    retry.policy.branch-rollback-on-conflict = true
    }
    report.retry.count = 5
    table.meta.check.enable = false
    report.success.enable = true
    }
    tm {
    commit.retry.count = 5
    rollback.retry.count = 5
    }
    undo {
    data.validation = true
    log.serialization = "jackson"
    log.table = "undo_log"
    }
    log {
    exceptionRate = 100
    }
    support {
    # auto proxy the DataSource bean
    spring.datasource.autoproxy = false
    }
    }
  • register.conf该文件是用于指定注册中心相关配置的,它有2个核心block

    • registry块
      此模板表明我们的注册中心使用类型,需要根据type设置来同步更新使用的配置信息,比如我们使用的eureka,因此我们更新eureka.serviceUrl="http://172.16.1.187:21001/eureka".

    • config 块
      该模块用来设置我们的配置中心相关配置(此处后续更新,我们会选择apollo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    registry {
    # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
    type = "eureka"

    nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
    }
    eureka {
    serviceUrl = "http://172.16.1.187:21001/eureka"
    application = "default"
    weight = "1"
    }
    redis {
    serverAddr = "localhost:6379"
    db = "0"
    }
    zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
    }
    consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
    }
    etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
    }
    sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
    }
    file {
    name = "file.conf"
    }
    }

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

    nacos {
    serverAddr = "localhost"
    namespace = ""
    }
    consul {
    serverAddr = "127.0.0.1:8500"
    }
    apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
    }
    zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
    }
    etcd3 {
    serverAddr = "http://localhost:2379"
    }
    file {
    name = "file.conf"
    }
    }

Client Configure for SEATA 1.0.0

在SEATA 1.0.0 中发布了一个新的feature,可以使用yaml/properties来替换掉我们在之前引入的file.confregistry.conf. 只需2步即可达到目的:

  • 第一步,更改依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!--seata-->
    <dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <exclusions>
    <exclusion>
    <artifactId>seata-all</artifactId>
    <groupId>io.seata</groupId>
    </exclusion>
    </exclusions>
    </dependency>
    <dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.0.0</version>
    </dependency>

    官方的说法是只需要依赖seata-spring-boot-starter即可,但是在实际的过程中,该依赖内部依然使用的是seata-all 0.9版本,会报错,因此我单独引入了seata-all 1.0.0,提交 github issue,

  • 第二步:修改配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
server:
port: 8001
# 1.0新添加的enabled激活自动配置,使得我们可以在yaml/properties文件中配置,
# 避免了以前需要客户端引入2个文件:
# file.conf 和 registry.conf
seata:
enabled: true # 1.0新特性,需要依赖seata-spring-boot-starter,默认为true
tx-service-group: geekplus_tx_group
# transport:
# type: TCP #default value is TCP
# server: NIO #default value is NIO
# heartbeat: true #enable heartbeat
# enable-client-batch-send-request: true
# serialization: seata
# compressor: none
# shutdown:
# wait: 3 #when destroy server, wait seconds
registry:
type: eureka
eureka:
service-url: http://localhost:8888/eureka
# application: default
# weight: 1
# service:
# vgroup-mapping: geekplus_tx_group
# disable-global-transaction: false
# disableGlobalTransaction: false
client:
support:
spring:
datasource-autoproxy: false
spring:
application:
name: seata-1-0-transaction
...

你可以在spring-configuration-metadata.json文件中找到相对应的配置属性. 在使用service属性时,同样存在1个问题,关于disableGlobalTransactiondisable-global-transaction都无法生效的问题。Git issue.
详情演示可查看demo/mscx-seata-1-0-demo