In the previous article, we introduced concepts related to distributed transactions, as well as several theoretical patterns. This article will start from practice, based on Seata AT mode, with the help of e-commerce order business scenario, to achieve an example.

Code examples for this article: gitee.com/zhaowenyi/s…

1. Preparation

This paper will purchase goods in the e-commerce system, deduct goods inventory, deduct account balance as the application scenario, based on Seata AT mode to achieve a simple case.

1.1 Framework Preparations

  • JDK 1.8
  • Nacos Server 1.4.2
  • Seata Server 1.4.2

1.2 Database Preparation


-- -------------------------------- 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;
  
-- Mandatory UNdo_log table for seATA framework
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;


- inventory table
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0.PRIMARY KEY (`id`),
  UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO storage_tbl (commodity_code, count) VALUES ("product_001", 10);
INSERT INTO storage_tbl (commodity_code, count) VALUES ("product_002", 20);
INSERT INTO storage_tbl (commodity_code, count) VALUES ("product_003", 30);

- orders table
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  `money` int(11) DEFAULT 0.PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Account balance statement
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `money` int(11) DEFAULT 0.PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into account_tbl (user_id, money) VALUES (1.1000);
insert into account_tbl (user_id, money) VALUES (2.2000);
insert into account_tbl (user_id, money) VALUES (3.3000);
Copy the code

1.3 Scaffolding Preparation

Scaffolding provided by Aliyun is used in this article

Start.aliyun.com/bootstrap.h…

After entering group and project name, next select component dependencies

Build dependencies:

  • Spring Cloud Alibaba Seata
  • Nacos Service Discovery
  • Arthas
  • Spring boot devtools
  • Lombok
  • Spring Configuration Processor
  • MyBatis Plus Framework
  • Validation
  • Junit
  • Spring web

You can take a look at the code

Click Get code to get the code down

1.4 Seata installation

Seata. IO /zh-cn/docs/…

  • Download Seata Server at github.com/seata/seata…

  • Drag to the bottom, select the zip package to download and unzip

  • Go to the bin directory and run the following command to start seATA. The default port is 8091
$ sh ./bin/seata-server.sh
Copy the code
  • After the startup is successful, the following is displayed

1.5 Nacos start

For the Nacos installation, go to the bin directory of Nacos and execute the following command

$ sh startup.sh -m standalone
Copy the code

After the startup is successful, the following is displayed

The console address is: http://localhost:8848/nacos/

1.6 Seata integration with Nacos

If Seata uses Nacos as the configuration center, relevant configuration changes will be required. Where Registry.conf configures the mode of registry and configuration center, the default is file, and nacOS will be used as the configuration center and registry in this article.

  • Go to the conf directory of Seata and modify the contents of registry

  • Change type from file to nacos, and configure the username and password for nacos. Note that both Registry and config need to be changed to nacOS

  • Modify the contents of file. Conf

Change type to db and set the user name and password for the local data

  • Download nacos config

In github.com/seata/seata… Download nacos-config.sh and put it in the conf directory

In github.com/seata/seata… TXT file and put it in the root directory (not the conf directory)

  • Modify the config. TXT file

In the conf directory, run sh nacos-config.sh 127.0.0.1, where 127.0.0.1 is the address of nacOS

Then look at the NACOS configuration center and see that the configuration has been published to NacOS

  • Final registry. Conf file
Registry {# file, NACOS, Eureka, Redis, ZK, Consul, ETCD3, SOFA type ="nacos"

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_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"
    aclToken = ""
  }
  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 = "nacos"
    password = "nacos"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }
  apollo {
    appId = "seata-server"
    ## apolloConfigService will cover apolloMeta
    apolloMeta = "http://192.168.1.204:8801"
    apolloConfigService = "http://192.168.1.204:8080"
    namespace = "application"
    apolloAccesskeySecret = ""
    cluster = "seata"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
    nodePath = "/seata/seata.properties"
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"}}Copy the code
  • The final file.conf file

service {
  #transaction service group mapping
  vgroupMapping.seata-demo-account="default"
  vgroupMapping.seata-demo-order="default"
  vgroupMapping.seata-demo-storage="default"
  default.grouplist="127.0.0.1:8091"disableGlobalTransaction=false } ## transaction log store, only used in seata-server store { ## store mode: File, db, redis mode ="db"
  ## rsa decryption public key
  publicKey = ""
  ## 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"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "JDBC: mysql: / / 127.0.0.1:3306 / spring_cloud_alibaba? rewriteBatchedStatements=true"
    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 {## redis mode: single, sentinel mode ="single"
    ## single mode property
    single {
      host = "127.0.0.1"
      port = "6379"
    }
    ## sentinel mode property
    sentinel {
      masterName = ""
      ## such as "10.28.235.65:26379,10.28. 235.65:26380,10.28. 235.65:26381"
      sentinelHosts = ""
    }
    password = ""
    database = "0"
    minConn = 1
    maxConn = 10
    maxTotal = 100
    queryLimit = 100}}Copy the code
  • The final config. TXT
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.seata-demo-storage=default
service.vgroupMapping.seata-demo-order=default
service.vgroupMapping.seata-demo-account=default
service.default.grouplist=127.0. 01.: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.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=file
store.lock.mode=file
store.session.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 / spring_cloud_alibaba? useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
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. 01.
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

Copy the code
  • Start the Seata

Run the./seata-server.sh -h 127.0.0.1 -p 8091 -m db command in the bin directory, where 8091 is the seata server port

1.7 Project Configuration

  • Copy file.conf and registry. Conf above to the resource directory of each project

  • Modify application.properties to add nacOS configuration and data source configuration
# app name
spring.application.name=seata-demo-storage
# Application service WEB access port
server.port=8080
# Nacos Help documentation: https://nacos.io/zh-cn/docs/concepts.html
# Nacos authentication information
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos service discovery and registration configuration, where the sub-attribute server-addr specifies the Nacos server host and port
spring.cloud.nacos.discovery.server-addr=localhost:8848
Register with the specified namespace of nacOS. Default is public
spring.cloud.nacos.discovery.namespace=public

spring.cloud.alibaba.seata.tx-service-group=seata-demo-storage

# datasource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/spring_cloud_alibaba? useUnicode=true&&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
Copy the code
  • After the project is successfully started, the following logs are generated: Now that we have completed the basic preparation work, it is time to develop the business logic.
2021-08-13 17:14:56.131  INFO 81427 --- [  restartedMain] c.a.c.n.registry.NacosServiceRegistry    : nacos registry, DEFAULT_GROUP seata-demo-storage 172.1838.14.:8080 register finished
2021-08-13 17:14:56.156  INFO 81427 --- [  restartedMain] c.e.s.SeataDemoStorageApplication        : Started SeataDemoStorageApplication in 13.745 seconds (JVM running for 17.071)
Copy the code

2. Architecture design

Business logic for users to purchase goods. The entire business logic is supported by three microservices:

  • Storage service: deduct the storage quantity for a given item.
  • Order service: Creates orders based on purchasing requirements.
  • Account service: deducts balances from user accounts.

3. Seata-demo-account account service

3.1 AccountController

/ * * *@ClassName : AccountController
 * @Description: account *@Author : zhaowenyi
 * @Date: 2021/08/13 * /
@RestController
public class AccountController {

    @Autowired
    AccountTblService accountTblService;

    @PostMapping("/api/account/debit")
    public void debit(@RequestParam("userId") String userId,
                      @RequestParam("money")  int money) { accountTblService.debit(userId, money); }}Copy the code

3.2 AccountController

/** * account */
@Service
public class AccountTblServiceImpl extends ServiceImpl<AccountTblMapper.AccountTbl>
implements AccountTblService {

    @Override
    public void debit(String userId, int money) {
        if(StringUtils.equals(userId, "2")) {
            throw new RuntimeException("Simulated anomaly");
        }
        var wrapper = new LambdaUpdateWrapper<AccountTbl>();
        wrapper.setSql("money = money - " + money)
                .eq(AccountTbl::getUserId, userId);
        this.update(wrapper); }}Copy the code

4. Seata-demo-storage Inventory service

4.1 StorageController


/ * * *@ClassName : StorageController
 * @Description: Inventory services *@Author : zhaowenyi
 * @Date: 2021/08/13 * /
@RestController
public class StorageController {

    @Autowired
    StorageTblService storageTblService;

    @PostMapping(value = "/api/storage/debuct")
    public void debuct(@RequestParam(value = "commodityCode", required = true) String commodityCode,
                       @RequestParam(value = "count", required = true) Integer count) { storageTblService.deduct(commodityCode, count); }}Copy the code

4.2 StorageTblServiceImpl

/** * inventory */
@Service
public class StorageTblServiceImpl extends ServiceImpl<StorageTblMapper.StorageTbl>
implements StorageTblService {

    @Override
    public void deduct(String commodityCode, int count) {
        var wrapper = new LambdaUpdateWrapper<StorageTbl>();
        wrapper.setSql("count = count - " + count)
                .eq(StorageTbl::getCommodityCode, commodityCode);
        this.update(wrapper); }}Copy the code

5. Seata-demo-storage Inventory service

5.1 OrderController


/ * * *@ClassName : OrderController
 * @Description: Order service *@Author : zhaowenyi
 * @Date: 2021/08/13 * /
@RestController
public class OrderController {

    @Autowired
    OrderTblService orderTblService;

    @PostMapping("api/order/create")
    public void create(String userId, String commodityCode, int orderCount) { orderTblService.create(userId, commodityCode, orderCount); }}Copy the code

5.2 OrderTblServiceImpl

To implement global transactions, annotate the @GlobalTransactional annotation when creating the order

/** ** Order service */
@Service
public class OrderTblServiceImpl extends ServiceImpl<OrderTblMapper.OrderTbl>
implements OrderTblService {

    @Autowired
    AccountFeignClient accountFeignClient;

    @Autowired
    StorageFeignClient storageFeignClient;

    @GlobalTransactional
    @Override
    public OrderTbl create(String userId, String commodityCode, int orderCount) {
        OrderTbl order = new OrderTbl();
        order.setCommodityCode(commodityCode);
        order.setCount(orderCount);
        order.setMoney(orderCount * 100);
        order.setUserId(userId);
        this.save(order);
        
        // Deduct the balance
        accountFeignClient.debit(userId, order.getMoney());
        // Deduct inventory
        storageFeignClient.debuct(commodityCode, orderCount);
        return null; }}Copy the code

Test 6.

6.1 Annotate @globalTransactional annotations

  • Initialized data

  • User 1 purchases product product_001
curl --location --request POST 'localhost:8082/api/order/create? userId=1&orderCount=1&commodityCode=product_001' \
--header 'Cookie: JSESSIONID=BEA387BFD6640DE11364C8A10C11A85C'
Copy the code

There is an extra piece of data in the order table

The account list is missing 100

The inventory table is minus 1

  • Simulating an exception, user 2 purchases product product_001

Failed to invoke debit account balance

feign.FeignException$InternalServerError: [500] during [POST] to [http://seata-demo-account/api/account/debit?userId=2&money=100] [AccountFeignClient#debit(String,int)]: [{" timestamp ":" the 2021-08-13 T12: though. 571 + 00:00 ", "status" : 500, "error" : "Internal Server Error","trace":"java.lang.RuntimeException: Simulating abnormal \ n \ tat com. Elio. Seatademoaccount. Impl. AccountTblServiceImpl. Debit (Acc... the at (5528 bytes)] Feign. FeignException. ServerErrorStatus (FeignException. Java: 231) ~ [feign - core - 10.10.1. Jar: na] the at Feign. FeignException. ErrorStatus (FeignException. Java: 180) ~ [feign - core - 10.10.1. Jar: na] the at Feign. FeignException. ErrorStatus (FeignException. Java: 169) ~ [feign - core - 10.10.1. Jar: na] the at Feign. Codec. ErrorDecoder $Default. Decode (ErrorDecoder. Java: 92) ~ [feign - core - 10.10.1. Jar: na] the at Feign. AsyncResponseHandler. HandleResponse (AsyncResponseHandler. Java: 96) ~ [feign - core - 10.10.1. Jar: na] the at Feign. SynchronousMethodHandler. ExecuteAndDecode (SynchronousMethodHandler. Java: 138) ~ [feign - core - 10.10.1. Jar: na] the at Feign. SynchronousMethodHandler. Invoke (SynchronousMethodHandler. Java: 89) ~ [feign - core - 10.10.1. Jar: na] the at Feign. ReflectiveFeign $FeignInvocationHandler. Invoke (ReflectiveFeign. Java: 100) ~ [feign - core - 10.10.1. Jar: na] the at com.sun.proxy.$Proxy98.debit(Unknown Source) ~[na:na] at com.elio.seatademoorder.impl.OrderTblServiceImpl.create(OrderTblServiceImpl.java:38) ~[classes/:na]Copy the code

There is an extra piece of data in the order table

No money is deducted from the account form at this time!!

Inventory table also does not deduct inventory!!

This indicates that the order creation service is not an atomic operation. The order creation succeeds, and the account balance deduction and inventory deduction fail.

6.2 Annotated @globalTransactional

When creating an order with the @GlobalTransactional annotation, simulate an exception

The order table does not create a new order at this time

The balance is not deducted from the account

Looking at the log, Seata rolled back the order service after the call to deduct the account balance failed

2021- 08 -13 20:17:01.110  INFO 91060 --- [ch_RMROLE_1_1_8] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=172.18. 41122.:8091:7458126500631044112,branchId=7458126500631044115,branchType=AT,resourceId=jdbc:mysql://localhost:3306/spring_cloud_alibaba,applicationData=null
2021- 08 -13 20:17:01.112  INFO 91060 --- [ch_RMROLE_1_1_8] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 172.18. 41122.:8091:7458126500631044112 7458126500631044115 jdbc:mysql://localhost:3306/spring_cloud_alibaba
2021- 08 -13 20:17:01.196  INFO 91060 --- [ch_RMROLE_1_1_8] i.s.r.d.undo.AbstractUndoLogManager      : xid 172.18. 41122.:8091:7458126500631044112 branch 7458126500631044115, undo_log deleted with GlobalFinished
2021- 08 -13 20:17:01.198  INFO 91060 --- [ch_RMROLE_1_1_8] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_Rollbacked
2021- 08 -13 20:17:01.224  INFO 91060 --- [nio-8082-exec-2] i.seata.tm.api.DefaultGlobalTransaction  : [172.18. 41122.:8091:7458126500631044112] rollback status: Rollbacked
2021- 08 -13 20:17:01.263 ERROR 91060 --- [nio-8082-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() forservlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;  nested exception is feign.FeignException$InternalServerError: [500] during [POST] to [http://seata-demo-account/api/account/debit? userId=2&money=100] [AccountFeignClient#debit(String,int)]: [{" timestamp ":" the 2021-08-13 T12:17:01. 050 + 00:00 ", "status" : 500, "error" : "Internal Server Error","trace":"java.lang.RuntimeException: Simulating abnormal \ n \ tat com. Elio. Seatademoaccount. Impl. AccountTblServiceImpl. Debit (Acc... (5528 bytes)]] with the root cause

feign.FeignException$InternalServerError: [500] during [POST] to [http://seata-demo-account/api/account/debit? userId=2&money=100] [AccountFeignClient#debit(String,int)]: [{" timestamp ":" the 2021-08-13 T12:17:01. 050 + 00:00 ", "status" : 500, "error" : "Internal Server Error","trace":"java.lang.RuntimeException: Simulating abnormal \ n \ tat com. Elio. Seatademoaccount. Impl. AccountTblServiceImpl. Debit (5528 bytes)] (Acc...
	at feign.FeignException.serverErrorStatus(FeignException.java:231) ~[feign-core-10.101..jar:na]
	at feign.FeignException.errorStatus(FeignException.java:180) ~[feign-core-10.101..jar:na]
	at feign.FeignException.errorStatus(FeignException.java:169) ~[feign-core-10.101..jar:na]
	at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:92) ~[feign-core-10.101..jar:na]
	at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:96) ~[feign-core-10.101..jar:na]
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-10.101..jar:na]
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89) ~[feign-core-10.101..jar:na]
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-10.101..jar:na]
	at com.sun.proxy.$Proxy99.debit(Unknown Source) ~[na:na]
	at com.elio.seatademoorder.impl.OrderTblServiceImpl.create(OrderTblServiceImpl.java:38) ~[classes/:na]

Copy the code

conclusion

This article demonstrates the power of Seata in a very simple case study in e-commerce. In addition to the time it takes to install Seata initially, subsequent distributed transactions only need to be annotated with the @GlobalTransactional annotation interface, making it truly invasing-free and fast. But how Seata achieves distributed transaction control is still a black box for us. I will delve deeper into how Seata works in the following articles