SpringCloud transaction management AT transaction management II

Posted by morgan on Sat, 22 Jan 2022 17:05:24 +0100

AT transaction of spring cloud transaction

1seata server TC global transaction coordinator

Seata Server - TC global transaction coordinator

AT transaction has three roles: TC (Transaction Coordinator), TM (transaction manager) and RM (Resource Manager). TM and RM are embedded in business applications, while TC is an independent service.

Seata Server is TC, which can be downloaded and started directly from the official warehouse. Download address: https://github.com/seata/seata/releases

Seata Server configuration

There are two profiles for Seata Server:

  • seata/conf/registry.conf
  • seata/conf/file.conf

Seata Server needs to register with the registry so that other services can discover and communicate with Seata Server through the registry.

Seata supports a variety of registry services: nacos, eureka, redis, zk, consumer, etcd3 and sofa.

In our project, we need to use the eureka registry, the connection address of eureka service and the registered service name, which need to be in registry Configure in the conf file:

registry.conf file

registry {
  # file ,nacos ,eureka,redis,zk,consul,etcd3,sofa
  # Select eureka registration configuration here
  type = "eureka"

  nacos {
	......
  }

  # Registration configuration of eureka
  eureka {
    # Address of Registration Center
    serviceUrl = "http://localhost:8761/eureka"
    # Registered service ID
    application = "seata-server"
    weight = "1"
  }
  
  redis {
	......
  }
  ......

file.conf
Seata needs to store global transaction information, branch transaction information and global lock information. Where are these data stored?

For the configuration of storage location, it can be placed in the configuration center or in local files. The configuration center services supported by Seata Server include: nacos, apollo, zk, consumer, etcd3.

Here we choose the simplest one and use the local file, which needs to be in registry Conf configuration file

file.conf configures the storage location of transaction information. The storage location supports file, db and redis.

Here, we choose the database as the storage location, which needs to be in file Configure in conf:

store {
  ## store mode: file,db,redis
  # Select database storage here
  mode = "db"

  ## file store property
  file {
  	......
  }

  # Database storage
  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"

	# Database connection configuration
    url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"
    user = "root"
    password = "root"
    minConn = 5
    maxConn = 30

	# Transaction log table name setting
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"

    queryLimit = 100
    maxWait = 5000
  }

  ## redis store property
  redis {
  	......
  }
}

The recipient will be seata-server-1.3.0 Download the zip file, put it into a file directory that does not contain Chinese, and unzip it

2 startup parameter setting

Enter the bin directory and find Seata server The bat file is opened using a text file, and 85 lines are found,

Change the memory configuration (the purpose is to reduce the memory configuration and save memory)

%JAVACMD% %JAVA_OPTS% -server -Xmx2048m -Xms2048m -Xmn1024m -Xss512k -XX:Sur......

Change to

%JAVACMD% %JAVA_OPTS% -server -Xmx256m -Xms256m -Xmn128m -Xss512k -XX:Sur......

The data used for the storage of this project is stored, so you need to modify the file in conf Contents stored in files in conf

Modify the file in the conf directory Files in conf

Modify registry. In the conf directory Conf file

Start Seata Server

Start the Eureka registry first

Enter the bin directory of Seata Server and double-click Seata Server Bat starts the Seata Server.

To view the registration information of Seata Server in Eureka registry:

Add Seata AT transaction to order service

Order calls inventory and account. Let's start with the previous order.

To start a global transaction in an order item, you also need to execute the branch transaction of order saving.

3. Add seata dependency

Add seata dependency to order parent

POM of order parent There is a commented out seata dependency in the XML file, and you can open it now

POM of order parent The contents of the XML file are as follows:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>order-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order-parent</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
        <druid-spring-boot-starter.version>1.1.23</druid-spring-boot-starter.version>
        <seata.version>1.3.0</seata.version>
        <spring-cloud-alibaba-seata.version>2.0.0.RELEASE</spring-cloud-alibaba-seata.version>
        <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid-spring-boot-starter.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-seata</artifactId>
            <version>${spring-cloud-alibaba-seata.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>${seata.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4. Configure the order module

Configure application in order YML file, TC transaction coordinator organizes multiple services into a global transaction through "transaction group". When each service starts, it needs to register with TC and join the same transaction group.

Add a transaction group in the order module

spring:
  ......
  
  cloud:
    alibaba:
      seata:
        tx-service-group: order_tx_group

......

You need to obtain the address of TC from the registry. Configure the address of the registry here.

registry.conf file content

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

  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
  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"
    session.timeout = 6000
    connect.timeout = 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,springCloudConfig
  type = "file"

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

file. Configuration in conf

Here we specify the service ID Seata server of TC:

vgroupMapping.order_tx_group = "seata-server"

order_tx_group corresponds to application Transaction group name registered in YML.

# Which coordinator does the transaction group use
# Transaction group name = coordinator service name
vgroupMapping.order_tx_group = "seata-server"

file.conf file content

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  # the client batch send request enable
  enableClientBatchSendRequest = true
  #thread factory for netty
  threadFactory {
    bossThreadPrefix = "NettyBoss"
    workerThreadPrefix = "NettyServerNIOWorker"
    serverExecutorThread-prefix = "NettyServerBizHandler"
    shareBossWorker = false
    clientSelectorThreadPrefix = "NettyClientSelector"
    clientSelectorThreadSize = 1
    clientWorkerThreadPrefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    bossThreadSize = 1
    #auto default pin or 8
    workerThreadSize = "default"
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}
service {
  #transaction service group mapping
  # order_ tx_ Group is consistent with the configuration of "TX service group: order_tx_group" in yml
  # "Seata server" is consistent with the registered name of TC server
  # Get the address of Seata server from eureka, register yourself with Seata server and set group

  # Which coordinator does the transaction group use
  # Transaction group name = coordinator service name
  vgroupMapping.order_tx_group = "seata-server"

  #only support when registry.type=file, please don't set multiple addresses
  order_tx_group.grouplist = "127.0.0.1:8091"
  #degrade, current not support
  enableDegrade = false
  #disable seata
  disableGlobalTransaction = false
}

client {
  rm {
    asyncCommitBufferLimit = 10000
    lock {
      retryInterval = 10
      retryTimes = 30
      retryPolicyBranchRollbackOnConflict = true
    }
    reportRetryCount = 5
    tableMetaCheckEnable = false
    reportSuccessEnable = false
  }
  tm {
    commitRetryCount = 5
    rollbackRetryCount = 5
  }
  undo {
    dataValidation = true
    logSerialization = "jackson"
    logTable = "undo_log"
  }
  log {
    exceptionRate = 100
  }
}

Document storage

5 create seata data source agent

Seata AT transaction has no intrusion into business code and fully automates the processing of global transactions. Its function is realized by Seata's data source agent tool.

Here, we create the data source proxy of Seata and exclude the default data source of Spring.

package cn.tedu.order;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
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 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);
    }
}

Exclude the default data source of Springboot from the main program:

Add the following comments on the main program

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

Because the source configuration url of seat is inconsistent with datasource, it should be in application Add a new data source connection to YML

jdbcUrl: jdbc:mysql://localhost:3307/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8

6 start global transaction

Seata AT has no business intrusion, so it is very simple to start a global transaction. You only need to add a @ GlobalTransactional annotation.

In addition, we add global transactions step by step and test them. Here, we note out the storage and account calls.

Enter CN. In the order module tedu. order. Servie package, find the OrderServiceImpl implementation class, and add global transactions to the create method,

@GlobalTransactional

7 start up test

Start services in order:

1.Eureka

2.Seata Server

3.Easy Id Generator

4.Order

Call to save the order, address:

http://localhost:8083/create?userId=1&productId=1&count=10&money=100

Observe the console to see that the global transaction and the branch transaction of the order have been started, and you can see the global transaction ID (XID) and branch transaction ID (Branch ID):

Then observe the newly added order data in the database:

8 simulate rollback

Test exceptions and rollback

Add a simulated exception to the OrderServiceImpl business code and try again:

throw new RuntimeException("Simulation anomaly");

Restart the order project and call save order:

http://localhost:8083/create?userId=1&productId=1&count=10&money=100

You can see the log of global transaction rollback:

After the test, you can comment out the simulation exception

9 add SeataAT transactions to the storage module

First, give the storage application YML file configuration transaction group

  #The group name of the transaction group
  cloud:
    alibaba:
      seata:
        tx-service-group: order_tx_group

Put the file in the resiurces folder in the order module Conf and registry Copy the conf file to the resources folder in the storage module.

Create seata data source proxy

Add CN. In the order module tedu. Copy the DSAutoConfiguration under the order package to CN. Under the storage module tedu. Storage package

Exclude the DataSourceAutoConfiguration autoconfiguration class from the main program annotation

Add on the main startup class

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

In application Add a new seat data source configuration in the YML configuration file

jdbcUrl: jdbc:mysql://localhost:3307/seata_storage?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8

Start branch transaction

Add @ Transactional annotation on business method to start local transaction:

Under the storage item CN tedu. storage. Add @ Transactional annotation to the decrease method under the StorageServiceImpl implementation class in the service package

package cn.tedu.storage.service;

import cn.tedu.storage.mapper.StorageMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class StorageServiceImpl implements StorageService{

    @Autowired
    private StorageMapper storageMapper;

    @Transactional
    @Override
    public void decrease(Long productId, Integer count) {
        storageMapper.decrease(productId,count);
    }
}

In the OrderServiceImpl business code of order, call the item to reduce the inventory

Previously, we commented out the calling commodity inventory, and now open the comment:

10 start the storage project

Start projects in order:

1.Eureka

2.Seata Server

3.Easy Id Generator

4.Storage

5.Order

Call to save the order, address:

http://localhost:8083/create?userId=1&productId=1&count=10&money=100

Add a new piece of data to the order database

Data reduction in storage database

The order will call inventory, and the two services will start a branch transaction respectively. The two branch transactions together form a global transaction:

Observe that the console of the two projects has the log of Seata AT transactions. The console of the Storage project is as follows:

Console of order module

11 simulation abnormality

Add a simulated exception to the business code in the storage module and try again:

throw new RuntimeException("Simulation anomaly");

Restart the storage project and call save order:

http://localhost:8083/create?userId=1&productId=1&count=10&money=100

The Order module rolled back

An exception occurred in the storage module

It can be concluded that the data is rolled back

After the storage module is tested, remove the comments

11account module adds SeataAT transaction

First, in the account module, click appplication To add a transaction group to YML, add it under spring configuration

#The group name of the transaction group
cloud:
  alibaba:
    seata:
      tx-service-group: order_tx_group

File. In the resources folder of the order project conf,registry. The conf file is copied to the same folder as account

Create seata data source proxy

The same as the data source agent in order items and inventory items, the order module CN tedu. Copy the DSAutoConfiguration in the order package to CN. Of account tedu. In account

Exclude the DataSourceAutoConfiguration configuration from the main startup class, and add the following annotation to the main startup class

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

Because the database url attribute name of hikari is JDBC url, not url, you need to add a configuration of JDBC url in the configuration file

jdbcUrl: jdbc:mysql://localhost:3307/seata_account?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8

Start branch transaction

Annotate the local transaction on the AccoutServiceImpl business

Invoke the amount of deducted account in the business class of order

We commented out the calling account earlier, and now open the comment:

12 start the account project for testing

Start projects in order:

Eureka

Seata Server

Easy Id Generator

Storage

Account

Order

Call to save the order, address:

http://localhost:8083/create?userId=1&productId=1&count=10&money=100

The order will call inventory and account, and the three services will start a branch transaction respectively. The three branch transactions together form a global transaction:

Observe that the console of the three projects has the log of Seata AT transactions. The console of the account project is as follows:

order console

Storage Console

Account console

Then observe the order table, inventory table and account table in the database.

order table

storage table

account table

13 abnormal test Account

Add exception information in the business class accountserviceimpl of Account

throw new RuntimeException("Simulation anomaly");

Restart the account project and call save order:

http://localhost:8083/create?userId=1&productId=1&count=10&money=100

Check the database tables order, storage and account. If the execution is successful, new orders will be added, inventory will be reduced and amount will be deducted. If the execution fails, the data will not change and will be rolled back.

In case of failure, you can see the rollback log in the order, storage and account console.

order console

Stopage console

account console

View database tables

account table

order table

Stopage table

After completion, remove the exception comment of account

Transaction completion for SeataAT