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