• Spring Alibaba Nacos + Seata 1.4.0搭建使用


    在学习springcloud系统继承seata1.4.0的过程中,踩了不少的坑。因此记录一下,以便记忆。

     

    第一步:搭建Nacos

    参考官网:https://nacos.io/zh-cn/docs/quick-start.html

    windows环境:

    1.下载nacos

    下载地址:https://github.com/alibaba/nacos/releases

    我选择的是1.4.0版本

     

    2.启动服务

    以单机模式模式启动:startup.cmd -m standalone

    当然也可以修改startup.cmd文件直接点击启动

    这样便显示启动nacos成功。

    访问:http://localhost:8848/nacos/

    出现下图:

    nacos的默认账号密码是:nacos/nacos

     

     

    第二步:搭建seata环境

    1.下载seata

    下载地址:https://github.com/seata/seata/releases

    我下载的是1.4.0版本

     

    2.搭建数据库

    seata-server

    搭建seata应用前,需要创建一个数据库。数据库名为: seata

    但是从seata1.0.0版本(好像是这个)开始,不再自带数据库脚本。因此需要下载source文件,或者到github上找。

    数据库脚本地址:https://github.com/seata/seata/blob/develop/script/server/db/mysql.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-server的数据库最终是这样:

    seata-client

    seata-client为自己测试的服务,可自由发挥。但是每个事务的数据库中都要添加一张undo_log表。

    脚本地址是:https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql

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

    业务表参考:

    # 订单库
    create database seata_order;
    ​
    # 订单表
    CREATE TABLE t_order(
    `id` bigint(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
    `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
    `count` int(11) DEFAULT NULL COMMENT '数量',
    `money` decimal(11,0) DEFAULT NULL COMMENT '金额',
    `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中1:已完结'
    )ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARACTER SET=utf8;
    ​
    ​
    #库存库
    create database seata_storage;
    ​
    CREATE TABLE t_storage(
    `id` bigint(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
    `total` int(11) DEFAULT NULL COMMENT '总库存',
    `used` int(11) DEFAULT NULL COMMENT '已用库存',
    `residue` int(11) DEFAULT NULL COMMENT '剩余库存'
    )ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARACTER SET=utf8;
    ​
    insert into seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`)
    values('1','1','100','0','100');
    ​
    ​
    #账户库
    create database seata_account;
    ​
    CREATE TABLE t_account(
    `id` bigint(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
    `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
    `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
    `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',
    `residue` DECIMAL(10,0) DEFAULT NULL COMMENT '剩余可用额度'
    )ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARACTER SET=utf8;
    ​
    insert into seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`)
    values('1','1','1000','0','1000');
     

    执行完脚本的效果图:

    3.修改文件配置

    config.txt

    下载地址:https://github.com/seata/seata/blob/develop/script/config-center/config.txt

    修改

    transport.type=TCP
    transport.server=NIO
    transport.heartbeat=true
    transport.enableClientBatchSendRequest=false
    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_test_tx_group=default
    service.default.grouplist=127.0.0.1:8091
    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.tm.commitRetryCount=5
    client.tm.rollbackRetryCount=5
    client.tm.defaultGlobalTransactionTimeout=60000
    client.tm.degradeCheck=false
    client.tm.degradeCheckAllowTimes=10
    client.tm.degradeCheckPeriod=2000
    store.mode=file
    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=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
    store.redis.mode=single
    store.redis.single.host=127.0.0.1
    store.redis.single.port=6379
    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
    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

    将本文件复制到seata根目录下:

    nacos-config.sh

    下载地址:https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh

    #!/usr/bin/env bash
    # Copyright 1999-2019 Seata.io Group.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at、
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    while getopts ":h:p:g:t:u:w:" opt
    do
      case $opt in
      h)
        host=$OPTARG
        ;;
      p)
        port=$OPTARG
        ;;
      g)
        group=$OPTARG
        ;;
      t)
        tenant=$OPTARG
        ;;
      u)
        username=$OPTARG
        ;;
      w)
        password=$OPTARG
        ;;
      ?)
        echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] "
        exit 1
        ;;
      esac
    done
    ​
    urlencode() {
      for ((i=0; i < ${#1}; i++))
      do
        char="${1:$i:1}"
        case $char in
        [a-zA-Z0-9.~_-]) printf $char ;;
        *) printf '%%%02X' "'$char" ;;
        esac
      done
    }
    ​
    if [[ -z ${host} ]]; then
        host=localhost
    fi
    if [[ -z ${port} ]]; then
        port=8848
    fi
    if [[ -z ${group} ]]; then
        group="SEATA_GROUP"
    fi
    if [[ -z ${tenant} ]]; then
        tenant=""
    fi
    if [[ -z ${username} ]]; then
        username=""
    fi
    if [[ -z ${password} ]]; then
        password=""
    fi
    ​
    nacosAddr=$host:$port
    contentType="content-type:application/json;charset=UTF-8"
    ​
    echo "set nacosAddr=$nacosAddr"
    echo "set group=$group"
    ​
    failCount=0
    tempLog=$(mktemp -u)
    function addConfig() {
      curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null
      if [[ -z $(cat "${tempLog}") ]]; then
        echo " Please check the cluster status. "
        exit 1
      fi
      if [[ $(cat "${tempLog}") =~ "true" ]]; then
        echo "Set $1=$2 successfully "
      else
        echo "Set $1=$2 failure "
        (( failCount++ ))
      fi
    }
    ​
    count=0
    for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do
      (( count++ ))
        key=${line%%=*}
        value=${line#*=}
        addConfig "${key}" "${value}"
    done
    ​
    echo "========================================================================="
    echo " Complete initialization parameters,  total-count:$count ,  failure-count:$failCount "
    echo "========================================================================="if [[ ${failCount} -eq 0 ]]; then
        echo " Init nacos config finished, please start seata-server. "
    else
        echo " init nacos config fail. "
    fi

    将该文件复制到seata/conf文件夹下面:

    添加config.txt的配置到nacos

    注意这里要将config.txt的配置添加到nacos上面,打开git bash here

    执行命令:sh nacos-config.sh -h 127.0.0.1

    刷新nacos,可以看到config.txt的配置注册到nacos上面了:

     

    file.conf:

    找到D:developserverseataseata-server-1.4.0seataconf位置下的file.conf文件修改。必须修改的地方有:

    store.mode="db"

    store.db.url= "jdbc:mysql://127.0.0.1:3306/seata"

    store.db.user = "root"

    store.db.password = "123456"

    ## transaction log store, only used in seata-server
    store {
      ## store mode: file、db、redis
      mode = "db"## file store property
      file {
        ## store location dir
        dir = "sessionStore"
        # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
        maxBranchSessionSize = 16384
        # globe session size , if exceeded throws exceptions
        maxGlobalSessionSize = 512
        # file buffer size , if exceeded allocate new buffer
        fileWriteBufferCacheSize = 16384
        # when recover batch read size
        sessionReloadReadSize = 100
        # async, sync
        flushDiskMode = async
      }
    ​
      ## database store property
      db {
        ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
        datasource = "druid"
        ## mysql/oracle/postgresql/h2/oceanbase etc.
        dbType = "mysql"
        driverClassName = "com.mysql.jdbc.Driver"
        url = "jdbc:mysql://127.0.0.1:3306/seata"
        user = "root"
        password = "123456"
        minConn = 5
        maxConn = 100
        globalTable = "global_table"
        branchTable = "branch_table"
        lockTable = "lock_table"
        queryLimit = 100
        maxWait = 5000
      }
    ​
      ## redis store property
      redis {
        host = "127.0.0.1"
        port = "6379"
        password = ""
        database = "0"
        minConn = 1
        maxConn = 10
        maxTotal = 100
        queryLimit = 100
      }
    ​
    }

    registry.conf

    同样找到D:developserverseataseata-server-1.4.0seataconf位置下的file.conf文件修改。必须修改的地方有:

    registry.type="nacos" #因为我用的注册中心是nacos,所以改为nacos

    registry.nacos.username= "nacos"

    registry.nacos.password= "nacos"

    registry.config.type= "nacos"

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      type = "nacos"
      loadBalance = "RandomLoadBalance"
      loadBalanceVirtualNodes = 10
    ​
      nacos {
        application = "seata-server"
        serverAddr = "127.0.0.1:8848"
        group = "DEFAULT_GROUP"
        namespace = ""
        cluster = "default"
        username = "nacos"
        password = "nacos"
      }
      eureka {
        serviceUrl = "http://localhost:8761/eureka"
        application = "default"
        weight = "1"
      }
      redis {
        serverAddr = "localhost:6379"
        db = 0
        password = ""
        cluster = "default"
        timeout = 0
      }
      zk {
        cluster = "default"
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
      }
      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 = "nacos"
    ​
      nacos {
        serverAddr = "127.0.0.1:8848"
        namespace = ""
        group = "SEATA_GROUP"
        username = ""
        password = ""
      }
      consul {
        serverAddr = "127.0.0.1:8500"
      }
      apollo {
        appId = "seata-server"
        apolloMeta = "http://192.168.1.204:8801"
        namespace = "application"
        apolloAccesskeySecret = ""
      }
      zk {
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
      }
      etcd3 {
        serverAddr = "http://localhost:2379"
      }
      file {
        name = "file.conf"
      }
    }

    4.启动服务

    windows:

    直接双击seata-server.bat:

    成功启动后,可以在nacos上面看到:

     

    第三步:搭建客户端服务

    关于服务端的代码太多,就贴出关键部分的。要想知道全部代码,可以参考我的github: https://github.com/BestLmc/maomao/tree/master/seata-orader-service2001

    OrderService

    pom:

    <dependencies>
            <!--nacos-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-client</artifactId>
                <version>1.3.2</version>
            </dependency>
            <!--seata-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                <exclusions>
                    <exclusion>
                        <artifactId>seata-all</artifactId>
                        <groupId>io.seata</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-all</artifactId>
                <version>1.4.0</version>
            </dependency>
            <!--feign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <!--web-actuator-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--mysql-druid-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.37</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
        </dependencies>

    application.yml

    server:
      port: 2001
    ​
    spring:
      application:
        name: seata-order-service
      cloud:
    #    alibaba:
    #      seata:
    #        #自定义事务组名称需要与seata-server中的对应
    #        tx-service-group: default
        nacos:
          discovery:
            server-addr: localhost:8848
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/seata_order
        username: root
        password: 123456
    ​
    seata:
      enabled: true
      application-id: ${spring.application.name}
      enable-auto-data-source-proxy: false
      tx-service-group: SEATA_GROUP
      service:
        vgroupMapping:
          SEATA_GROUP: default
      config:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
          group : "SEATA_GROUP"
          namespace: ""
          username: "nacos"
          password: "nacos"
      registry:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
          namespace: ""
          group: SEATA_GROUP
          cluster: default
    ​
    feign:
      hystrix:
        enabled: false
    ​
    logging:
      level:
        io:
          seata: info
    ​
    mybatis:
      mapperLocations: classpath:mapper/*.xml

    OrderServiceImpl

    /**
       * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
       * 简单说:下订单->扣库存->减余额->改状态
       */
    @Override
    @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
    public void create(Order order)
    {
        log.info("----->开始新建订单");
        //1 新建订单
        orderDao.create(order);
    ​
        //2 扣减库存
        log.info("----->订单微服务开始调用库存,做扣减Count");
        storageService.decrease(order.getProductId(),order.getCount());
        log.info("----->订单微服务开始调用库存,做扣减end");
    ​
        //3 扣减账户
        log.info("----->订单微服务开始调用账户,做扣减Money");
        accountService.decrease(order.getUserId(),order.getMoney());
        log.info("----->订单微服务开始调用账户,做扣减end");
    ​
        //4 修改订单状态,从零到1,1代表已经完成
        log.info("----->修改订单状态开始");
        orderDao.update(order.getUserId(),0);
        log.info("----->修改订单状态结束");
    ​
        log.info("----->下订单结束了,O(∩_∩)O哈哈~");
    ​
    }

    启动服务

    启动相关服务,如图:

    注意:由于测试的时候2002和2003的端口被占用了,因此就改为其他的端口测试。

    接下来就可以进行seata的事务测试了!

     

     

  • 相关阅读:
    Android 操作系统架构开篇
    《构建之法》读后感
    《梦断代码》读后感
    学习日报
    学习日报
    记账本开发4
    记账本开发3
    学习日报
    学习日报
    记账本开发2
  • 原文地址:https://www.cnblogs.com/bestlmc/p/14315180.html
Copyright © 2020-2023  润新知