1. Introduce Seata

Seata is an open source distributed transaction solution dedicated to providing high performance and easy to use distributed transaction services. Seata will provide users with AT, TCC, SAGA and XA transaction modes to create a one-stop distributed solution for users.

Before the open source of Seata, the internal version of Seata has been playing the role of distributed consistency middleware in Ali economy, helping the economy to smoothly go through the Double 11 of each year and providing strong support for BU businesses. After years of precipitation and accumulation, commercial products have been sold on Ali Cloud and Financial cloud. In order to create a more complete technology ecosystem and inclusive technology achievements, Seata officially announced open source. In the future, Seata will help its technology in the form of community construction more reliable and complete.

  • Microservices framework support

Currently, it supports RPC frameworks such as Dubbo, Spring Cloud, Sofa-RPC, Motan and Grpc, and other frameworks are being continuously integrated

  • AT mode

Provides non-intrusive automatic compensation transaction mode, currently supports MySQL, Oracle, PostgreSQL and TiDB AT mode, in H2 development

  • TCC mode

    Support TCC mode and can be mixed with AT, higher flexibility

  • SAGA mode

    Provide efficient solutions for long transactions

  • XA mode

    Support XA schema for databases that have implemented XA interfaces

  • High availability

    Supports cluster mode based on database storage and has strong horizontal expansion capability

2. Install and start seata-Server

2.1 Download and Installation

This document uses Seata1.4.2 as an example. Zk is used for the registry and Apollo is used for the configuration center

Seata. IO/useful – cn/blog /…

2.2 Initializing the Database

CREATE TABLE `branch_table` (
  `branch_id` bigint(20) NOT NULL.`xid` varchar(128) NOT NULL.`transaction_id` bigint(20) DEFAULT NULL.`resource_group_id` varchar(32) DEFAULT NULL.`resource_id` varchar(256) DEFAULT NULL.`branch_type` varchar(8) DEFAULT NULL.`status` tinyint(4) DEFAULT NULL.`client_id` varchar(64) DEFAULT NULL.`application_data` varchar(2000) DEFAULT NULL.`gmt_create` datetime(6) DEFAULT NULL.`gmt_modified` datetime(6) DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `global_table` (
  `xid` varchar(128) NOT NULL.`transaction_id` bigint(20) DEFAULT NULL.`status` tinyint(4) NOT NULL.`application_id` varchar(32) DEFAULT NULL.`transaction_service_group` varchar(32) DEFAULT NULL.`transaction_name` varchar(128) DEFAULT NULL.`timeout` int(11) DEFAULT NULL.`begin_time` bigint(20) DEFAULT NULL.`application_data` varchar(2000) DEFAULT NULL.`gmt_create` datetime DEFAULT NULL.`gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`.`status`) USING BTREE,
  KEY `idx_transaction_id` (`transaction_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `lock_table` (
  `row_key` varchar(128) NOT NULL.`xid` varchar(96) DEFAULT NULL.`transaction_id` bigint(20) DEFAULT NULL.`branch_id` bigint(20) NOT NULL.`resource_id` varchar(256) DEFAULT NULL.`table_name` varchar(32) DEFAULT NULL.`pk` varchar(36) DEFAULT NULL.`gmt_create` datetime DEFAULT NULL.`gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`row_key`),
  KEY `idx_branch_id` (`branch_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code

2.3 configuration

2.3.1 registry. Conf

Registry {type = "zk" zk {# cluster name cluster = "default" serverAddr = "XXX :1500" sessionTimeout = 60000 connectTimeout = 20000 } } config { type = "apollo" apollo { appId = "arch-seata-server" ## apolloConfigService will cover apolloMeta apolloMeta = "http://xxx:8080" apolloConfigService = "http://xxx:8080" namespace = "application" apolloAccesskeySecret = -denv =FAT cluster = "test" seata = "test"}}Copy the code

2.3.2 Apollo configuration

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
store.mode = db
store.db.datasource = druid
store.db.dbType = mysql
store.db.driverClassName = com.mysql.jdbc.Driver
store.db.url = jdbc:mysql://xxx:3306/server_seata? rewriteBatchedStatements=true
store.db.user = 
store.db.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
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
server.undo.logSaveDays = 7
server.undo.logDeletePeriod = 86400000
log.exceptionRate = 100
transport.serialization = seata
transport.compressor = none
metrics.enabled = false
metrics.registryType = compact
metrics.exporterList = prometheus
metrics.exporterPrometheusPort = 9898
Copy the code

2.4 start

Add -denv =FAT when bat.sh is started

3. The client

3.1 the pom

< the dependency > < groupId > IO. Seata < / groupId > < artifactId > seata - spring - the boot - starter < / artifactId > < version > 1.4.2 < / version > </dependency>Copy the code

3.2 Initializing the Database (AT Mode)

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id'.`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 NOT NULL COMMENT 'create datetime'.`log_modified` datetime NOT NULL COMMENT 'modify datetime',
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`.`branch_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
Copy the code

3.3 configuration

# service.vgroupMapping.my_test_tx_group = service.vgroupMapping.my_test_tx_group
seata.tx-service-group = my_test_tx_group
Seata-server cluster name = seata-server cluster name
service.vgroupMapping.my_test_tx_group = default

seata.registry.type = zk
# seata. Registry. Zk. Cluster = default this configuration on the client side is invalid
seata.registry.zk.server-addr = xxx:2181
seata.registry.zk.session-timeout = 60000
seata.registry.zk.connect-timeout = 20000

seata.config.type = apollo
seata.config.apollo.apollo-meta = http://xxx:8080
seata.config.apollo.app-id = arch-seata-debug
seata.config.apollo.namespace = application
The following configuration can be independent
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.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.
client.undo.dataValidation = true
client.undo.logSerialization = jackson
client.undo.onlyCareUpdateColumns = true
client.undo.logTable = undo_log
client.undo.compress.enable = true
client.undo.compress.type = zip
client.undo.compress.threshold = 64k
#RoundRobinLoadBalance,RandomLoadBalance,LeastActiveLoadBalance,ConsistentHashLoadBalance
client.loadBalance.type = RandomLoadBalance
client.loadBalance.virtualNodes = 10
log.exceptionRate = 100
transport.serialization = seata
transport.compressor = none

Copy the code

vm options

-Denv=XXX -Dapollo.cluster=test -Dapollo.meta=http://xxx:8080 -Dconfig.apollo.seata=test -Dconfig.apollo.apolloConfigService=http://xxx:8080

3.4 the AT mode

@GlobalTransactional(name = "testAT")
public void testAT(a) {
   // Local transaction
    dosql();
   / / RPC calls
    dorpc();
}
Copy the code

3.5 TCC mode

@GlobalTransactional(name = "testTCC")
public void testTCC(a) {
    / / RPC or local
   TccActionTwoImpl.prepare(null."a".new ArrayList());
    / / RPC or local
    TccActionOneImpl.prepare(null);
}


@LocalTCC
public interface TccActionTwo{
     /**
     * Prepare boolean. TwoPhaseBusinessAction.name =branch.resourseId
     *
     * @param actionContext the action context
     * @paramB the B will be put into the actionContext of the BusinessActionContext and can be retrieved by the annotated name@paramList the list is put into the actionContext of BusinessActionContext *@return the boolean
     */
      @TwoPhaseBusinessAction(name = "DubboTccActionTwo", commitMethod = "commit", rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext,
                           @BusinessActionContextParameter(paramName = "b") String b,
                           @BusinessActionContextParameter(paramName = "c", index = 1) List list);
    
    boolean commit(BusinessActionContext var1);

    boolean rollback(BusinessActionContext var1);
}

@LocalTCC
public interface TccActionOne{
      @TwoPhaseBusinessAction(name = "DubboTccActionOne", commitMethod = "commit", rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext);
    
    boolean commit(BusinessActionContext var1);

    boolean rollback(BusinessActionContext var1);
}
Copy the code

4.SEATA-SERVER

4.1 Architecture Model

  • TC (Transaction Coordinator) – Transaction Coordinator
    • Maintains the state of global and branch transactions and drives global transaction commit or rollback.
    • Corresponding Seata – server
  • TM (Transaction Manager) – Transaction Manager
    • Define the scope of a global transaction: start, commit, or roll back the global transaction.
  • RM Resource Manager (RM) – Resource Manager
    • Manage resources for branch transaction processing, talk to TCS to register branch transactions and report status of branch transactions, and drive commit or rollback of branch transactions.

4.2 Seata – server overview

TCS are responsible for maintaining the state of global and branch transactions and driving global transaction commit or rollback. Real-time awareness of the execution and state of the entire distributed transaction is required. The whole distributed transaction is composed of each executing node, and the transaction executed by each node is called branch transaction. All branch transactions registered on a global transaction together make up all the information for a distributed transaction. Branch transactions can be executed when a global transaction is committed or rolled back.

4.2.1 Global Transaction status table

state code note
Global Transaction Start (Begin) 1 This state can accept new branch transaction registrations
Research global transaction commit (Research case) 2 This state can change at any time
Global transaction CommitRetry 3 Try to retry the commit after the commit exception has been resolved
Rollbacking global transaction rollback 4 Rolling back the global transaction again
Global transaction RollbackRetry (RollbackRetrying) 5 Try transaction retry rollback after the global rollback exception has been resolved
Global transaction timeout rollback 6 Global transaction timeout rollback
Global transaction timeout rollback Retry (TimeoutRollbackRetrying) 7 Global transaction timeout rollback retry
Asynccommit (Asyncresearch) 8 Committing asynchronously
Phase 2 Committed 9 Phase two has committed and the global transaction state does not change after this state
Phase-two Commit Failure 10 Phase 2 commit failed
Rollbacked Two-phase Resolution global Rollback 11 Phase 2 resolution global rollback
Phase-2 global RollbackFailed (RollbackFailed) 12 Phase-2 global rollback failed. Procedure
Two-phase TimeoutRollback backed 13 Phase-2 timeout rollback
Phase-2 TimeoutRollbackFailed (TimeoutRollbackFailed) 14 Phase-2 timeout rollback failed. Procedure
Finished 15 Global end of transaction
UnKnown State 0 An unknown state

4.2.2 Branch transaction status table

state code note
Branch transaction Registration (Registered) 1 Register branch transactions with TCS
Branch transaction completes phase 1 (PhaseOne_Done) 2 Branch transaction phase one business logic is completed
PhaseOne_Failed Branch transaction phase 1 failed 3 Branch transaction phase 1 business logic failure
Phase 1 timeout for branch transactions (PhaseOne_Timeout) 4 Branch transaction one-phase processing timeout
Branch transaction Phase 2 committed 5 Branch transaction two-phase commit
Branch transaction phase-2 commit failure Retry (PhaseTwo_CommitFailed_Retryable) 6 The branch transaction failed phase 2 commit and retry
Branch transaction phase-2 commit failure do not retry (PhaseTwo_CommitFailed_Unretryable) 7 The branch transaction fails phase 2 commit and does not retry
Branch transaction Phase 2 Rolled back (PhaseTwo_Rollbacked) 8 The branch transaction phase 2 has been rolled back
Branch transaction phase-2 rollback failed retry (PhaseTwo_RollbackFailed_Retryable) 9 Branch transaction Phase-2 rollback failed and retry
Branch transaction phase-2 rollback failure not retry (PhaseTwo_RollbackFailed_Unretryable) 10 Phase 2 commit failed
UnKnown State 0 An unknown state

4.3 SeATa-Server Scheduled Task

4.3.1 Retry Rolling back global transactions that have timed out

  • Overridden global transaction objects:
    • Rollbacking 4 is rolling back the global transaction
    • Global Transaction RollbackRetry (RollbackRetrying) 5 Try transaction retry rollback after the global rollback exception is resolved
    • TimeoutRollbacking 6 The global transaction timeout is being rolled back
    • Global transaction timeout RollbackRetry (TimeoutRollbackRetrying) 7 Global transaction timeout rollback retry
  • Interval: server. Recovery. RollbackingRetryPeriod = 1000 ms
  • Conditions of execution:
    • Timeout: now-begintime > timeout
    • Maximum retries: server. MaxRollbackRetryTimeout, 1 for always try again
  • Execution: doGlobalRollback ()

4.3.2 Retry global transactions that commit timeout

  • Overridden global transaction objects:
    • Research (global transaction commit 2) this state changes any time
    • Global transaction CommitRetry 3 try to retry the commit after the commit exception is resolved
  • Interval: server.recovery.com mittingRetryPeriod = 1000 ms
  • Conditions of execution:
    • Timeout: now-begintime > timeout
    • Maximum retries: server. MaxCommitRetryTimeout, 1 for always try again
  • Execution: doGlobalCommit ()

4.3.3 Commit global transaction global transaction

  • Overridden global transaction objects:
    • Asynccommit (8) Asynchronous commit
  • Interval: server. Recovery. AsynCommittingRetryPeriod = 1000 ms
  • Execution: doGlobalCommit ()

4.3.4 Checking global transactions with transaction timeout

  • Overridden global transaction objects:

    • UnKnown Status 0 UnKnown status
    • Global Transaction Start (Begin) 1 This state can accept new branch transaction registrations
    • Research (global transaction commit 2) this state changes any time
    • Global transaction CommitRetry 3 try to retry the commit after the commit exception is resolved
    • Rollbacking 4 is rolling back the global transaction
    • Global Transaction RollbackRetry (RollbackRetrying) 5 Try transaction retry rollback after the global rollback exception is resolved
    • TimeoutRollbacking 6 The global transaction timeout is being rolled back
    • Global transaction timeout RollbackRetry (TimeoutRollbackRetrying) 7 Global transaction timeout rollback retry
    • Asynccommit (8) Asynchronous commit
  • Interval: server. Recovery. AsynCommittingRetryPeriod = 1000 ms

  • Execution: timeout is @GlobalTransactional annotation metadata, default 6000ms sets the transaction state to TimeoutRollbacking and adds it to the RETRY_ROLLBACKING_SESSION_MANAGER rollback queue

4.3.5 Clearing scheduled Undo_log Tasks

  • Interval: server. Undo. LogDeletePeriod undo clear thread interval default 86400000, unit of milliseconds
  • Execution condition: server.undo. LogSaveDays Undo retention days The default value is 7
  • Run the following command to clear undo_log,log_status=1, and uncleared undo

5.SEATA registered RM and TM

5.1 APP Startup Analysis

In the spring container startup, will scan to automatically configure SeataAutoConfiguration. The class, thus initialize GlobalTransactionScanner. Class

//GlobalTransactionScanner.java
private void initClient(a) {
        if (LOGGER.isInfoEnabled()) {
        LOGGER.info("Initializing Global Transaction Clients ... ");
        }
        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
        throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }
        //init TM
        TMClient.init(applicationId, txServiceGroup, accessKey, secretKey);
        if (LOGGER.isInfoEnabled()) {
        LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }
        //init RM
        RMClient.init(applicationId, txServiceGroup);
        if (LOGGER.isInfoEnabled()) {
        LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }

        if (LOGGER.isInfoEnabled()) {
        LOGGER.info("Global Transaction Clients are initialized. ");
        }
        registerSpringShutdownHook();

        }
Copy the code

As can be seen from the above source code, each APP is both TM and RM.

5.2 the TM registration

Below is a sequence diagram of TM registration, during which applicationId and transactionServiceGroup are sent to TCS. The TC saves the session to the Map for maintaining the heartbeat.

5.3 the RM registered

During registration, the RM sends resourceIds, applicationId, and transactionServiceGroup to the TC. The TC saves the session and takes out the session when the CORRESPONDING RM notifies the TC.

In particular, resourceIds are a collection of resourceIds

  • In AT mode it is equal to the database instance
  • In TCC mode this is equal to the value of name in @twophaseBusinessAction (Name = “DubboTccActionTwo”)

6. Summary

This chapter mainly introduces the origin of Seata, using ZK + Apollo to start seATa-server and Seata-client. On the TC side, you saw how global and branch transactions are defined in Seata and how these transaction state changes are driven by several scheduled tasks. On the client side, it briefly describes how to register itself with the TC at startup. In later articles, you’ll see how to use each pattern in Seata and what to be aware of.

Reference: seata. IO/useful – cn/docs /…

Recommended reading

Guava Cache actual Combat – From scenario to principle analysis

Details of HTTP2.0 and HTTPS protocols

Wechat official account

The article is published synchronously, the public number of political cloud technology team, welcome to pay attention to