The background,

In the previous section, we learned about cluster deployment of Seata. In this article, we use SpringBoot to integrate Seata for distributed transactions, using Seata’s AT mode.

Two, to achieve the function

We have two service account services, account-service and order service, which invoke account service in order service.

Invoking the account service in the order service is done through the RestTemplate.

Test scenario:

1. The account service is normal, and the order service is normal. Result: The account service is normal, and the order is generated.

2. The account service is normal and the order service is normal, but an exception occurs in the whole distributed transaction. Result: The account service does not deduct money and no order is generated.

The technologies used by each service

1. Account service

SpringBoot, Seata, Mybatis, Nacos, Druid

2. Order service

SpringBoot, Seata, Mybatis, NACOS, Hikari

Seata-spring-boot-starter is used to implement SpringBoot Seata, not seata-all.

4. Service realization

1. Account service implementation

Account service, which provides a simple function to deduct the account balance, is relatively simple.

Note:

1. Enable automatic data source agent.

2. Introducing Druid eliminates the need to automatically configure data sources.

3. Pay attention to transaction groups

1. Introduce jar packages

Only a few core packages are introduced here. The rest of the packages are not listed below, such as Mybatis, etc. Note that the integration with Seata uses seata-spring-boot-starter

<dependency>
  <groupId>io.seata</groupId>
  <artifactId>seata-spring-boot-starter</artifactId>
  <version>1.4.2</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.</version>
</dependency>
<dependency>
  <groupId>com.alibaba.nacos</groupId>
  <artifactId>nacos-client</artifactId>
  <version>1.3.2</version>
</dependency>
Copy the code

2. Project configuration

3. Build a predicate sentence

create database seata_account;
use seata_account;
create table account(
    id int unsigned auto_increment primary key comment 'primary key',
    name varchar(20) comment 'Username',
    balance bigint comment 'Account balance, in minutes'
) engine=InnoDB comment 'Account sheet';
insert into account(id,name,balance) values (1.'Joe'.100000);
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime'.UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB COMMENT ='AT transaction mode undo table';
Copy the code

Each business library must have an undo_log table

2. Order service implementation

Provides an interface for generating orders and deducting account balances.

Matters needing attention:

1. The order service disables the default data source agent and configures the data source agent itself.

2. Use Hikari data source to implement, because the AT mode is used, so you need to use DataSourceProxy to proxy data source.

3. The order service invokes the Account service using the RestTemplate, so you need to manually configure the Interceptor of the RestTemplate to implement the transmission of xIDS.

4. There is a bug in seata1.4.2. If the data type in seata business table is datetime, undolog may not be serialized successfully and can be processed by timestamp or other methods.

5. The undo_log table must exist in the service library.

1. Introduce jar packages

Druid is not introduced here. Note that seata-spring-boot-starter is used for integration with SeATA

<dependency>
  <groupId>io.seata</groupId>
  <artifactId>seata-spring-boot-starter</artifactId>
  <version>1.4.2</version>
</dependency>
<dependency>
  <groupId>com.alibaba.nacos</groupId>
  <artifactId>nacos-client</artifactId>
  <version>1.3.2</version>
</dependency>
Copy the code

2. Project configuration

3. Configure the data source agent

package com.huan.seata.config;

import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/ * * *@authorHuan. Fu 2021 1/9/24-10:34 am */
@Configuration
public class DataSourceConfig {
    
    @Autowired
    private DataSourceProperties dataSourceProperties;
    
    @Bean
    public DataSource dataSourceProxy(a) {
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setJdbcUrl(dataSourceProperties.getUrl());
        hikariDataSource.setUsername(dataSourceProperties.getUsername());
        hikariDataSource.setPassword(dataSourceProperties.getPassword());
        hikariDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        return newDataSourceProxy(hikariDataSource); }}Copy the code

In AT mode, the DataSourceProxy must be the DataSourceProxy.

4. Configure RestTemplate to pass the XID

@GlobalTransactional

@Service
@RequiredArgsConstructor
@Slf4j
public class BusinessServiceImpl implements BusinessService {
    
    private final OrderService orderService;
    private final RestTemplate restTemplate;
    
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void createAccountOrder(Integer accountId, Long amount, boolean hasException) {
        System.out.println("createAccountOrder:" + RootContext.getXID());
        // 1. Remote deduction of account balance
        remoteDebit(accountId, amount);
        
        // 2
        orderService.createOrder(accountId, amount);
        
        if (hasException) {
            throw new RuntimeException("There's an exception. Distributed things need to roll."); }}private void remoteDebit(Integer accountId, Long amount) {
        String url = "http://localhost:50001/account/debit? id=" + accountId + "&amount=" + amount;
        String result = restTemplate.getForObject(url, String.class);
        log.info("Remote deduction inventory result :[{}]", result); }}Copy the code

3. The transaction group needs to correspond to the configuration center

Here to order services to demonstrate, and configuration center corresponding on.

The transaction grouping for each service may be different, but needs to be aligned with the configuration center. For example, the configuration group of the order-service is: Tx -service-group=tx_order_service_group The service.vgroupMapping.tx_order_service_group=default configuration item must exist in the configuration center. Default is a cluster specified in the server configuration fileCopy the code

Five, presentations,

1. No abnormality occurs

Visit: http://localhost:50002/createOrder? accountId=1&amount=10&hasException=false

Create order normally, and deduct balance.

2. Exceptions occur

Visit: http://localhost:50002/createOrder? accountId=1&amount=10&hasException=true

No order is generated and no balance is deducted.

Six, possible problems

1. When Nacos serves as the Seata configuration center, an error is reported during project startup that the service cannot be found. How to troubleshoot, how to deal with?

A: exception: io.seata.com mon. Exception. FrameworkException: can not register the RM, err: can not connect to services – server.

  1. View the NACOS configuration list and check whether the SEATA configuration has been imported successfully
  2. View the nacOS service list and check whether serverAddr has been registered successfully
  3. Namespace. registrie.nacos. namespace and config.nacos.namespace: enter the namespace ID of nacos, default “”, On the server side and the client side, namespace public is a reserved control of NACOS. If you need to create your own namespace, you are advised not to have the same name as public, but to name it with a specific semantic name for a real business scenario
  4. Sh -p 8091 -h 122.51.204.197 -m file. For example, run sh seata-server.sh -p 8091 -h 122.51.204.197 -m file
  5. Check the seata/conf/nacos – config. TXT affairs grouping service. VgroupMapping. Trade_group = default configuration are consistent with the project group configuration name
  6. Telnet IP port Check whether the ports are open and the firewall status

2. What should be noted when using AT mode?

  1. Proxy data sources must be used, and there are three forms to proxy data sources:
  • When relying on seta-spring-boot-starter, the data source is automatically proxy without additional processing.
  • Rely on seata -all, use the @ EnableAutoDataSourceProxy (since 1.1.0) annotations, annotation parameters can choose the JDK or additional agents.
  • You can also use DatasourceProxy to wrap a DataSource manually if you rely on Seata-all.
  1. GlobalTransactionScanner needs to be configured manually when seata-all is used, but no additional operations are required when Seata-spring-boot-starter is used.
  2. A service table must contain single-column primary keys. If multiple primary keys exist, see Problem 13.
  3. Each service library must contain the undo_log table. If it is used with the database and table component, the database and table are not separated.
  4. Transactions across microservice links require support for the corresponding RPC framework, which is currently supported in SEata-ALL: Apache Dubbo, Alibaba Dubbo, SOFA -RPC, Motan, gRpc, httpClient, for Spring Cloud support, please refer to spring-Cloud-Alibaba-seata. Other self-developed frameworks, asynchronous models and message consumption transaction models should be supported by API.
  5. AT supports MySQL, Oracle, PostgreSQL, and TiDB.
  6. When a distributed transaction is enabled with annotations, if the default service provider joins the consumer transaction, the provider does not annotate the transaction. However, providers also require corresponding dependencies and configurations, and only annotations can be omitted.
  7. When a distributed transaction is started using annotations, if the transaction is required to be rolled back, an exception must be thrown to the originator of the transaction, which is aware of it by the @GlobalTransactional annotation. Provide directly throw an exception or define an error code for the consumer to judge and then throw an exception.

What do I need to notice when using the AT pattern with the Spring @Transactional annotation?

A laparoscope, usually connected to a @ Transactional with DataSourceTransactionManager and JTATransactionManager respectively local transactions and XA distributed transactions, we commonly used is combined with local affairs. When combined with local transactions, @Transactional and @GlobalTransaction are used together, @Transactional can only be at the same method level as the annotation in @GlobalTransaction or within the annotation method in @GlobalTransaction. The concept of a distributed transaction is more important than that of a local transaction. The @Transactional annotation on the outer layer will result in a distributed transaction being committed empty. When a connection corresponding to @Transactional is committed, the global transaction will be reported as being committed or its XID does not exist.

4. Dirty data cannot be rolled back because automatic timestamp update is enabled in the database

After SEATA records the current mirror due to service submission, the database updates the timestamp again, causing the mirror verification to fail.

** Solution 1: ** Turn off automatic timestamp updates for the database. Data timestamp updates, such as modification and creation times are maintained at the code level, such as MybatisPlus can be filled automatically.

Solution 2: Update statements do not place unupdated fields in update statements.

5. What are the restrictions on Seata using addresses registered in the registry?

Seata registries cannot register addresses 0.0.0.0 or 127.0.0.1, which can be specified by the startup parameter -h or the container environment variable SEATA_IP when automatically registered. If the registry is on a different network from the service, the registration address can be NAT_IP or a public IP address. Ensure that the health check of the registry is smooth.

Seata. IO /zh-cn/docs/…

Seven, code address

Code address: gitee.com/huan1993/sp…