The distributed transaction journey starting from installing Seata
- introduce
- Install Seata Server
- Integrate Seata in Spring Boot project
- Summarize
- References
introduce
What is Seata?
In modern application development, distributed systems are used more and more widely. However, as the complexity of the system increases, it becomes increasingly difficult to handle distributed transactions. This is the background in which Seata emerged. Seata (Simple Extensible Autonomous Transaction Architecture) is an open source distributed transaction solution designed to solve transaction consistency and coordination problems in distributed systems.
In traditional monolithic applications, relational databases are usually used to manage transactions and ensure data consistency. But in a distributed system, transaction management becomes complicated because multiple independent services are involved. The requirement of a distributed transaction is that all involved services are either successfully committed or rolled back to maintain data consistency.
Seata provides two main transaction modes:
-
AT Mode (Auto Compensation Mode): In AT mode, Seata will automatically compensate transactions without manually writing compensation logic. Seata will arrange all the operations of the transaction into a global transaction, then execute the try operation of each branch, and perform the corresponding compensation operation when an exception occurs.
-
TCC mode (two-phase commit mode): TCC mode requires developers to manually write the logic of the three phases of Try, Confirm and Cancel. In the TCC mode, Seata is responsible for coordinating the commit and rollback of global transactions, while the try, confirm and cancel operations of each branch are implemented by developers.
Seata also provides scalable registry and storage support, making it suitable for various scenarios.
In general, Seata is a powerful distributed transaction solution that can help developers solve distributed transaction problems and ensure data consistency and reliability in distributed systems. By integrating Seata, developers can more easily build complex distributed applications and improve system stability and performance.
Install Seata Server
Download the Seata Server release
You can download the latest Seata Server release from the Seata official website , and unzip it to a specified directory; there are source and binary versions, here we choose to install the binary file to download. My version is 1.7.0 (2023-07-11, recommended version)
Configure Seata
unzip files
The downloaded file is a zip file, and after decompression, it will be the folder above. The default folder name is seata.
Configure Seata's yml file
Enter seata/conf
the directory, there are two configuration files here; we need to application.yml
modify a name at will; and then application.example.yml
modify it application.yml
as the main configuration file.
Modify application.yml
the file, I use nacos here as the registration center, so the places that need to be modified are:
- seata:config:type
- seata:registry:type
- store:mode
- store:session:mode
- store: lock:mode
- store:db The configuration of the database is modified to its own
Configuration file:
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${
user.home}/logs/seata
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
# 这里是主要的配置文件
seata:
config:
# support: nacos 、 consul 、 apollo 、 zk 、 etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848
# 如果在nacos上添加了命名空间,则配置命令空间ID
namespace:
# 配置分组
group: SEATA_GROUP
username:
password:
context-path:
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
data-id: seataServer.properties
registry:
# support: nacos 、 eureka 、 redis 、 zk 、 consul 、 etcd3 、 sofa
type: nacos
preferred-networks: 30.240.*
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace:
cluster: default
username:
password:
context-path:
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
server:
service-port: 8091 #If not configured, the default is '${server.port} + 1000'
max-commit-retry-timeout: -1
max-rollback-retry-timeout: -1
rollback-retry-timeout-unlock-enable: false
enable-check-auth: true
enable-parallel-request-handle: true
retry-dead-threshold: 130000
xaer-nota-retry-timeout: 60000
enableParallelRequestHandle: true
recovery:
committing-retry-period: 1000
async-committing-retry-period: 1000
rollbacking-retry-period: 1000
timeout-retry-period: 1000
undo:
log-save-days: 7
log-delete-period: 86400000
session:
branch-async-queue-size: 5000 #branch async remove queue size
enable-branch-async-remove: false #enable to asynchronous remove branchSession
store:
# support: file 、 db 、 redis
mode: db
session:
mode: db
lock:
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: mysql
password: mysql
min-conn: 10
max-conn: 100
global-table: global_table
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 1000
max-wait: 5000
metrics:
enabled: false
registry-type: compact
exporter-list: prometheus
exporter-prometheus-port: 9898
transport:
rpc-tc-request-timeout: 15000
enable-tc-server-batch-send-response: false
shutdown:
wait: 3
thread-factory:
boss-thread-prefix: NettyBoss
worker-thread-prefix: NettyServerNIOWorker
boss-thread-size: 1
Load the configuration file config.txt to nacos
Modify the config.txt file
config.txt
The file is in seata/script/config-center
the directory; the places we need to modify are:
- store.mode=db
- store.lock.mode=db
- store.session.mode=db
- The configuration of the store.db database is modified to its own, which is the same as that in the above yml file
Load it on nacos
Enter seata/script/config-center/nacos
the directory to execute nacos-config.sh
the file.
sh nacos-config.sh -h 121.37.228.169 -p 8848 -g SEATA_GROUP -t 0af6e97b-a684-4647-b696-7c6d42aecce7 -u nacos -w nacos
- -h: Nacos IP address
- -p: Nacos port number
- -g: Group group name
- -t: Namespace ID, if not public by default
- -u: username
- -w: password
After the execution is completed, log in to our Nacos configuration management to view the loaded data:
Start the Seata service
Normal start
Enter seata/bin
the directory and execute the command:
sh seata-server.sh
Here I may encounter an abnormal situation when I start it. Below I have listed some pitfalls I encountered when starting it. can move to启动时遇到的坑
View startup log
My Mac system is open to open log files. Other systems need to execute commands according to the system, or directly enter the seata/logs
directory to view start.out
files.
open /Users/ddz/Downloads/seata/logs/start.out
You can see the log output address indicating that the startup is successful.
Open the console page
On the console page , the default account password is seata/seata.
Pit encountered at startup
Here are some pitfalls I encountered in deploying Seata, and there may be differences between them for reference only.
Unable to resolve value for ${console.user.username}
Exception information : Could not resolve placeholder 'console.user.username' in value “${console.user.username}”
Solution : You need to copy all the configuration information in the yml file that was previously modified to another name console
to the current application.yml
one . Here is to set the account password for our login console page.
Unable to parse the value of ${seata.security.secretKey}
Exception information : Could not resolve placeholder 'seata.security.secretKey' in value "${seata.security.secretKey}"
Solution : You need to copy all the configuration information in the yml file that was previously modified to another name seata.security
to the current application.yml
one .
database connection problem
Exception information : com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
In versions before MySQL5.7, the security is low, and any user can connect to the database, so the official version 5.7 has increased privacy protection of. And the default useSSL = true value is adopted to prevent random modification of the database. In version 8.0, SSL is still retained, and the default value is true.
Solution : Append after the database configuration url &useSSL=false
; you need to check the yml configuration file and the configuration list on Nacos store.db.url
.
Integrate Seata in Spring Boot project
Above we introduced how to install the seata server locally, and then introduce the integration of seata in our Spring Boot project; here I only use a demo to introduce it, which can be implemented according to your own business logic.
working environment
- MySQL 5.7.28
- Maven 3.5.4
- JDK 1.8
- Mybot 3.4.1
- dynamic 3.4.1
cloud, boot, alibaba environment
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
add dependencies
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 多数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- Seata分布式事务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
configuration yml
server:
port: 7001
spring:
application:
name: ddz-user
cloud:
nacos:
discovery:
# 服务分组
group: ddz
server-addr: 121.37.228.111:8848
# 必须填命名空间的ID
# namespace: 9ebef975-dcc0-4430-9c63-1c62d8a86d82
datasource:
dynamic:
# 开启seata分布式事务
seata: true
strict: false
primary: master
datasource:
master:
url: jdbc:mysql://121.37.228.111:3306/ddz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
username: ddz
password: ddz2023
local:
url: jdbc:mysql://127.0.0.1:3306/ddz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
username: root
password: ddz2023
# MyBatis Plus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.ddz.**.entity
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*.xml
global-config:
db-config:
id-type: auto
configuration:
# 开启驼峰,开启后,只要数据库字段和对象属性名字母相同,无论中间加多少下划线都可以识别
map-underscore-to-camel-case: true
# Seata 配置
seata:
application-id: seata-server
# 是否启用数据源bean的自动代理
enable-auto-data-source-proxy: false
tx-service-group: default_tx_group # 必须和服务器配置一样
registry:
type: nacos
nacos:
# Nacos 服务地址
server-addr: 121.37.228.111:8848
group: SEATA_GROUP
# namespace: 9ebef975-dcc0-4430-9c63-1c62d8a86d82
application: seata-server # 必须和服务器配置一样
# username:
# password:
cluster: default
config:
type: nacos
nacos:
server-addr: 121.37.228.111:8848
group: SEATA_GROUP
# namespace: 9ebef975-dcc0-4430-9c63-1c62d8a86d82
service:
vgroup-mapping:
default_tx_group: default # 必须和服务器配置一样
disable-global-transaction: false
client:
rm:
# 是否上报成功状态
report-success-enable: true
# 重试次数
report-retry-count: 5
Integrate Seata in business code
We create two mapper
classes based on two data sources and then controller
test them; I omit the business layer for the convenience of testing here.
Use @GlobalTransactional
annotations to manage global transactions
Demonstrates AT (automatic compensation) mode and TCC (two-phase commit) mode
Use AT (auto compensation) mode
In AT mode, Seata will automatically compensate transactions without manually writing compensation logic. First, let's demonstrate a simple transfer scenario, transferring funds from one account to another and ensuring transaction consistency.
- Add the @GlobalTransactional annotation
Add the @GlobalTransactional annotation to the method of the transfer service to mark the global transaction:
@Service
public class TransferService {
@GlobalTransactional
public void transfer(String fromAccount, String toAccount, double amount) {
// 扣除转出账户金额
deductAmount(fromAccount, amount);
// 增加转入账户金额
addAmount(toAccount, amount);
}
// 实现扣除金额逻辑
// ...
// 实现增加金额逻辑
// ...
}
- Test AT mode
Write test cases to verify transaction management in AT mode:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TransferServiceTest {
@Autowired
private TransferService transferService;
@Test
public void testTransfer() {
// 假设从账户 A 转账 100 到账户 B
transferService.transfer("accountA", "accountB", 100.0);
}
}
Run the test case to observe whether the transfer is successful, and check the log to confirm whether Seata automatically compensated the transaction.
Use TCC (two-phase commit) mode
In the TCC mode, we need to manually write the logic of the three phases of Try, Confirm and Cancel to ensure the correct execution of the transaction. Let's demonstrate a simple order creation scenario, including three stages: placing an order, deducting inventory, and creating an order.
- Implement the TCC interface
Create a TCC interface and implement the logic of the three stages of Try, Confirm and Cancel:
public interface OrderTccService {
@GlobalTransactional
boolean createOrder(OrderDTO orderDTO);
@TwoPhaseBusinessAction(name = "orderTccService", commitMethod = "confirmOrder", rollbackMethod = "cancelOrder")
boolean tryCreateOrder(OrderDTO orderDTO);
boolean confirmOrder(OrderDTO orderDTO);
boolean cancelOrder(OrderDTO orderDTO);
}
- Implement TCC logic
Write TCC logic in the implementation class:
@Service
public class OrderTccServiceImpl implements OrderTccService {
@Override
public boolean tryCreateOrder(OrderDTO orderDTO) {
// 预留库存逻辑
// ...
return true;
}
@Override
public boolean confirmOrder(OrderDTO orderDTO) {
// 确认创建订单逻辑
// ...
return true;
}
@Override
public boolean cancelOrder(OrderDTO orderDTO) {
// 取消创建订单逻辑
// ...
return true;
}
}
- Test the TCC mode
Write test cases to verify the transaction management of the TCC mode:
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderTccServiceTest {
@Autowired
private OrderTccService orderTccService;
@Test
public void testCreateOrder() {
// 创建一个订单
OrderDTO orderDTO = new OrderDTO();
// 设置订单信息
// ...
orderTccService.createOrder(orderDTO);
}
}
Run the test case to observe whether the order is successfully created, and check the log to confirm whether the Try, Confirm and Cancel phases of the TCC mode are executed correctly.
Integrate the pit that Seata stepped on
Here are some pitfalls I encountered during the integration process, and there may be differences between them for reference only.
Table ‘ddz.undo_log’ doesn’t exist
Exception information : process connectionProxy commit error: Table 'ddz.undo_log' doesn't exist.
Solution : Cut off the log table needed by Seata in the data source; add a new undo_log
table in each data source.
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;
no available service ‘null’ found, please make sure registry config correct
Exception information : The reason for this is that we did not load Seata config.txt
on nacos.
Solution : Execute the command to load the configuration to the Nacos configuration center. Refer to above:把配置文件config.txt加载到nacos上
Distributed transaction does not take effect
Reason : The multiple data sources we use dynamic
do not enable Seata
distributed transactions by default.
Solutiondynamic
: Add configuration under the yml configuration file seata: true
to enable distributed transactions.
dynamic-datasource can not find primary datasource
Reason : This is an error in the data source configuration information. The reason for this exception is that I am too careless to write the database connection (url) connection incorrectly.
Solution : Double check datasource
the configuration of the following data sources.
Communications link failure
Reason : Most of the reasons here are that MySQL needs to specify whether to make an SSL connection, and the SSL connection is enabled by default.
Solution : Just add it after the database connection configuration URL &useSSL=false
.
Summarize
As distributed systems continue to evolve, so will the field of distributed transactions. In the future, we can further explore more distributed transaction models and solutions to meet the needs of different business scenarios. At the same time, Seata, as an active open source project, will continue to introduce new features and improvements. We can pay attention to the updates of the Seata community and contribute our own strength.
In addition, in addition to Seata, there are other distributed transaction solutions, such as TCC-Transaction
, SAGA
, , HSTC
etc. These solutions are also worthy of our in-depth study and exploration. According to different business scenarios, we can choose the most suitable solution to solve the distributed transaction problem.
References
Nacos Official Documentation
Seata Official Documentation
Spring Boot Official Documentation