当前位置:网站首页>Seata handles distributed transactions
Seata handles distributed transactions
2022-04-23 18:38:00 【Hua Weiyun】
@[toc](SpringCloud Alibaba Seata Dealing with distributed transactions )
Source code download address :gitee Warehouse address
1、 Distributed transaction problems
Before distribution :
There is no such problem for single machine and single database .
After distribution :
Single applications are split into microservice applications , The original three modules are split into three independent applications , Use three separate data sources , Business operations need to call three services to complete . At this time, the data consistency within each service is guaranteed by local transactions , But the global data consistency cannot be guaranteed .
== namely : A business operation needs to be called remotely across multiple data sources or systems , There will be distributed transaction problems .==
2、Seata brief introduction
2.1 Seata What is it? ?
Seata Is an open source distributed transaction solution , Committed to providing high-performance and easy-to-use distributed transaction services .Seata Will provide users with AT、TCC、SAGA and XA Transaction mode , Create a one-stop distributed solution for users .
Official website address :https://seata.io/zh-cn/docs/overview/what-is-seata.html
2.2 Seata What can be done ?
A typical distributed transaction process : A part of distributed transaction processing ID+ Three component model
-
Transaction ID XID: Globally unique transaction ID
-
Transaction Coordinator (TC): Transaction coordinator , Maintain the running state of the global transaction , Responsible for coordinating and driving global transaction commit or rollback ;
-
Transaction Manager : Transaction manager , Control the boundaries of global transactions , Responsible for opening a global transaction , And finally initiate a global commit or global rollback resolution ;
-
Resource Manager (RM): Explorer , Control branch transactions , Responsible for branch registration 、 Status report , And receive instructions from the transaction coordinator , Drive branch ( Local ) Transaction commit and rollback
Treatment process :
(1) TM towards TC Request to open a global transaction , The global transaction is created successfully and generates a globally unique XID;
(2) XID Propagate in the context of the microservice call link ;
(3) RM towards TC Register branch transactions , Be included in the XID Jurisdiction corresponding to global transaction ;
(4) TM towards TC Launch against XID The global commit or rollback resolution for ;
(5) TC Dispatch XID All branch transactions under the jurisdiction complete the commit or rollback request .
2.3 download
Address :https://github.com/seata/seata/releases
I downloaded it 1.3.0 edition .
3、Seata-Server install
3.1 Seata-Server Of zip Extract the file and modify the configuration
modify seata-server-1.3.0\seata\conf Under directory file.conf file
Major changes : Custom transaction group name + The transaction log storage mode is db+ Database connection information
This 1.3.0 Why doesn't it default service 了 , We add it ourselves
service { #transaction service group mapping vgroupMapping.my_test_tx_group = "default" #only support when registry.type=file, please don't set multiple addresses default.grouplist = "127.0.0.1:8091" #degrade, current not support enableDegrade = false #disable seata disableGlobalTransaction = false}
Modify the log storage mode to db
mode = "db"
Modify database connection information ,Seata1.3.0 Version already supports mysql8.0 Database , Earlier versions are not supported .
## 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.cj.jdbc.Driver" url = "jdbc:mysql://localhost:3306/seata?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useSSL=false" user = "root" password = "123456" minConn = 5 maxConn = 30 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 }
3.2 mysql8.0 Database new database seata
3.3 stay seata Build a table in the library
Table creation in earlier versions sql Will be directly in conf The contents are given below , But the new version is not given , I'll find it here sql Put it underneath. :
db_store.sql
-- -------------------------------- The script used when storeMode is 'db' ---------------------------------- the table to store GlobalSession dataCREATE 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 dataCREATE 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 dataCREATE TABLE IF NOT EXISTS `lock_table`( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(96), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;
3.4 modify conf Under directory registry.conf The configuration file
Configure registry connection information , Here it is modified as Nacos
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" } ...}
3.5 Start the test
Start... First nacos
startup.cmd -m standalone
Restart seata-server
perform bin Under directory seata-server.bat
see seata Is registration successful
4、 Order / stock / Account business database preparation
The following tests need to be started first Nacos Restart Seata, Make sure both are OK .
4.1 Distributed transaction business description
Here we will create three services , An order service , An inventory service , An account service .
When the user places an order , An order will be created in the order service , Then the inventory service is called remotely to deduct the inventory of the ordered goods , Then the balance in the user account can be deducted by calling the account service remotely , Finally, modify the order status to completed in the order service .
This operation spans three databases , There are two remote calls , There's obviously a problem with distributed transactions .
summary : Place the order —> Inventory deduction —> Less account ( balance )
4.2 Create a business database
seata_order: Database to store orders ;
seata_storage: A database for storing inventory ;
seata_account: A database for storing account information .
CREATE DATABASE seata_order; CREATE DATABASE seata_storage; CREATE DATABASE seata_account;
4.3 Right up there 3 Create corresponding business tables for each library
seata_order Construction under reservoir t_order surface
CREATE TABLE t_order ( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` BIGINT(11) DEFAULT NULL COMMENT ' user id', `product_id` BIGINT(11) DEFAULT NULL COMMENT ' product id', `count` INT(11) DEFAULT NULL COMMENT ' Number ', `money` DECIMAL(11,0) DEFAULT NULL COMMENT ' amount of money ', `status` INT(1) DEFAULT NULL COMMENT ' The order status :0: In the create ;1: It's over ' ) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; SELECT * FROM t_order;
seata_storage Construction under reservoir t_storage surface
CREATE TABLE t_storage ( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `product_id` BIGINT(11) DEFAULT NULL COMMENT ' product id', `total` INT(11) DEFAULT NULL COMMENT ' Total inventory ', `used` INT(11) DEFAULT NULL COMMENT ' Used stock ', `residue` INT(11) DEFAULT NULL COMMENT ' Surplus stock ') ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO seata_storage.t_storage(`id`, `product_id`, `total`, `used`, `residue`)VALUES ('1', '1', '100', '0', '100'); SELECT * FROM t_storage;
seata_account Construction under reservoir t_account surface
CREATE TABLE t_account ( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id', `user_id` BIGINT(11) DEFAULT NULL COMMENT ' user id', `total` DECIMAL(10,0) DEFAULT NULL COMMENT ' Total amount ', `used` DECIMAL(10,0) DEFAULT NULL COMMENT ' Used balance ', `residue` DECIMAL(10,0) DEFAULT '0' COMMENT ' The remaining amount available ') ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO seata_account.t_account(`id`, `user_id`, `total`, `used`, `residue`) VALUES ('1', '1', '1000', '0', '1000'); SELECT * FROM t_account;
4.4 above 3 Create corresponding rollback log tables for each library
Order - stock - Account 3 Each database needs to build its own rollback log table
-- the table to store seata xid data-- 0.7.0+ add context-- you must to init this sql for you business databese. the seata server not need it.-- This script must be initialized in your current business database , be used for AT Pattern XID Record . And server End independent ( notes : Business database )-- Notice here 0.3.0+ Add unique index ux_undo_logDROP TABLE `undo_log`; 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;
5、 Order / stock / Account business micro service preparation
Business needs : Place the order -> Reduce inventory -> Deduct the balance -> Change ( Order ) state
5.1 New order Order-Module
5.1.1 newly build Module:seata-order-service2001
5.1.2 pom.xml
<dependencies> <!--nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <!-- Exclude built-in Seata --> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <!-- According to your Seata Select the version of the service --> <version>1.3.0</version> </dependency> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--web-actuator--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--mysql-druid--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
5.1.3 application.yml
server: port: 2001spring: application: name: seata-order-service cloud: alibaba: seata: enabled: true application-id: ${spring.application.name} tx-service-group: my_test_tx_group nacos: discovery: server-addr: localhost:8848 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/seata_order?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useSSL=false username: root password: 123456feign: hystrix: enabled: falselogging: level: io: seata: infomybatis: mapperLocations: classpath:mapper/*.xml
5.1.4 file.conf and registry.conf
We will configure the above file.conf Document and registry.conf Copy to project's resources Below directory
5.1.5 domain
OrderDao:
@Mapperpublic interface OrderDao { /** * Create order */ void create(Order order); /** * Modify order amount */ void update(@Param("userId") Long userId, @Param("status") Integer status);}
resources New under folder mapper Add... After folder OrderMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.atguigu.springcloud.alibaba.dao.OrderDao"> <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Order"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="user_id" property="userId" jdbcType="BIGINT"/> <result column="product_id" property="productId" jdbcType="BIGINT"/> <result column="count" property="count" jdbcType="INTEGER"/> <result column="money" property="money" jdbcType="DECIMAL"/> <result column="status" property="status" jdbcType="INTEGER"/> </resultMap> <insert id="create"> INSERT INTO `t_order` (`id`, `user_id`, `product_id`, `count`, `money`, `status`) VALUES (NULL, #{userId}, #{productId}, #{count}, #{money}, 0); </insert> <update id="update"> UPDATE `t_order` SET status = 1 WHERE user_id = #{userId} AND status = #{status}; </update></mapper>
5.1.6 Service Interface and its implementation
OrderService:
public interface OrderService { /** * Create order */ void create(Order order);}
OrderServiceImpl:
@Service@Slf4jpublic class OrderServiceImpl implements OrderService{ @Resource private OrderDao orderDao; @Resource private StorageService storageService; @Resource private AccountService accountService; /** * Create order -> Call inventory service to deduct inventory -> Call the account service to deduct the account balance -> Modify order status * In short : * Place the order -> Reduce inventory -> Less balance -> Change state */ @Override //@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class) public void create(Order order) { log.info("-------> Order start "); // This application creates orders orderDao.create(order); // Call inventory service remotely to deduct inventory log.info("------->order-service The deduction of inventory starts "); storageService.decrease(order.getProductId(),order.getCount()); log.info("------->order-service After deducting the inventory from the inventory, the end "); // Remote call account service deduction balance log.info("------->order-service The deduction balance starts "); accountService.decrease(order.getUserId(),order.getMoney()); log.info("------->order-service End of deduction balance in "); // Change order status to completed log.info("------->order-service Start by modifying the order status in "); orderDao.update(order.getUserId(),0); log.info("------->order-service End of modifying order status in "); log.info("-------> End of order "); }}
For the time being @GlobalTransactional Comment out , Let's demonstrate distributed transactions .
StorageService:
@FeignClient(value = "seata-storage-service")public interface StorageService { /** * Deducting the inventory */ @PostMapping(value = "/storage/decrease") CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);}
AccountService
@FeignClient(value = "seata-account-service")public interface AccountService { /** * Deduct account balance */ //@RequestMapping(value = "/account/decrease", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") @PostMapping("/account/decrease") CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);}
StorageService and AccountService The definition is to invoke the micro service of repository business and account business in order business. .
5.1.6 Controller
@RestControllerpublic class OrderController { @Autowired private OrderService orderService; /** * Create order */ @GetMapping("/order/create") public CommonResult create( Order order) { orderService.create(order); return new CommonResult(200, " Order created successfully !"); }}
5.1.7 Config To configure
MybatisConfig:
@Configuration@MapperScan({"com.atguigu.springcloud.alibaba.dao"})public class MyBatisConfig {}
DataSourceProxyConfig:
// Use Seata Proxy data sources @Configurationpublic class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } @Bean public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); return sqlSessionFactoryBean.getObject(); }}
5.1.8 Main startup class
@EnableDiscoveryClient@EnableFeignClients@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)// Cancel automatic creation of data source public class SeataOrderMainApp2001{ public static void main(String[] args) { SpringApplication.run(SeataOrderMainApp2001.class, args); }}
5.2 New inventory Storage-Module
The code will not be repeated , Go to the link address at the top of the article to download .
5.3 New account Account-Module
The code will not be repeated , Go to the link address at the top of the article to download .
6、 Distributed transaction testing
Place the order -> Reduce inventory -> Deduct the balance -> Change ( Order ) state
Initial status of the database
6.1 Simulate normal order
Start three microservices
Order normally :http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
Database situation :
SELECT * FROM `seata_order`.`t_order`
SELECT * FROM `seata_storage`.`t_storage`
SELECT * FROM `seata_account`.`t_account`;
You can see , There is no problem placing an order normally , Inventory decrease 10, Monthly decrease of account 100, The order status changes to 1.
6.2 Simulation timeout exception , No addition @GlobalTransactional annotation
AccountServiceImpl Add timeout
because 30 Seconds have passed OpenFeign Timeout for , So it will fail .
Revisit :http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
Database situation :
Oh my god , Failed to place an order, and even reduced the inventory and the user's account balance , That's good .
6.3 Timeout exception , add to @GlobalTransactional
AccountServiceImpl Add timeout
OrderServiceImpl add to @GlobalTransactional
annotation , The function of this annotation is to roll back whenever an exception occurs .
Visit at this time :http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
At this time, the database is in good condition :
)
You can see , There is no change in the database data after placing the order , No data was added , The distributed transaction rollback test was successful .
7、 Knowledge points supplement
Our default above is AT Transaction mode .
7.1 AT How to make the model non intrusive to the business
AT The premise of the pattern :
- Based on support for local ACID Relational database of transactions
- Java application , adopt JDBC Access database
The whole mechanism :
The evolution of the two-stage submission agreement :
- A stage : Business data and rollback logging are committed in the same local transaction , Release local locks and connection resources .
- Two stages :
- Submit asynchronized , Complete... Very quickly .
- Rollback is compensated by a phase of rollback log
7.2 Three components of distributed transaction
Execute the process :
-
TM Open distributed transaction (TM towards TC Register global transaction records );
-
By business scenario , Arrange the database 、 Services and other internal resources (RM towards TC Report resource readiness );
-
TM End distributed transaction , The end of the first phase of the business (TM notice TC Submit / Roll back distributed transactions );
-
TC Summarize transaction information , Decide whether a distributed transaction is committed or rolled back ;
-
TC Inform all RM Submit / Roll back resources , The second phase of business is over .
What I use here is Seata1.3.0 Version of , Many pits were also stepped on during the operation . This is just a simple tutorial , The real distributed transaction mechanism is very complex , More in-depth research is needed .
版权声明
本文为[Hua Weiyun]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231836021987.html
边栏推荐
- listener.log
- Machine learning practice - naive Bayes
- Notepad + + replaces tabs with spaces
- Dynamically add default fusing rules to feign client based on sentinel + Nacos
- Jeecg boot microservice architecture
- Custom prompt box MessageBox in QT
- CISSP certified daily knowledge points (April 15, 2022)
- 机器学习理论之(7):核函数 Kernels —— 一种帮助 SVM 实现非线性化决策边界的方式
- Using transmittablethreadlocal to realize parameter cross thread transmission
- Mysql database backup command -- mysqldump
猜你喜欢
Quantexa CDI(场景决策智能)Syneo平台介绍
Promote QT default control to custom control
昇腾 AI 开发者创享日全国巡回首站在西安成功举行
[mathematical modeling] - analytic hierarchy process (AHP)
Setting up keil environment of GD single chip microcomputer
Use Chenxi bookkeeping book to analyze the balance of revenue and expenditure of each account in a certain period of time
使用 bitnami/postgresql-repmgr 镜像快速设置 PostgreSQL HA
【ACM】70. climb stairs
Excel intercept text
Machine learning theory (7): kernel function kernels -- a way to help SVM realize nonlinear decision boundary
随机推荐
Nacos集群搭建和mysql持久化配置
Daily CISSP certification common mistakes (April 11, 2022)
Configure iptables
STM32 learning record 0008 - GPIO things 1
关于unity文件读取的操作(一)
Use stm32cube MX / stm32cube ide to generate FatFs code and operate SPI flash
STM32: LCD display
In shell programming, the shell file with relative path is referenced
Daily network security certification test questions (April 18, 2022)
WIN1 remote "this may be due to credssp encryption Oracle correction" solution
【数学建模】—— 层次分析法(AHP)
解决:cnpm : 无法加载文件 ...\cnpm.ps1,因为在此系统上禁止运行脚本
Usage of functions decode() and replace() in SQL
Ctfshow - web362 (ssti)
迁移学习进阶
纠结
Use of regular expressions in QT
Database computer experiment 4 (data integrity and stored procedure)
Analysez l'objet promise avec le noyau dur (Connaissez - vous les sept API communes obligatoires et les sept questions clés?)
【ACM】376. Swing sequence