In a single architecture, we mostly use a single database, which implements local transactions through the ACID feature support of the database. But distributed transaction is one of the inevitable problems in the complex business relationship under microservice architecture. Seata is Spring Cloud Alibaba distributed transaction Solution middleware, which solves distributed transaction problems in microservice scenarios. This article describes how to implement distributed transactions by setting up Seata environment and using its AT mode.

Version of the environment used in this article:

Nacos-server-1.3.1 seata-server-1.4.0 spring-cloud hoxton. SR3 spring-cloud-alibaba 2.2.1.RELEASE

1. Download Seata service installation package

Download seata – server – 1.4.0: https://github.com/seata/seata/releases/tag/v1.4.0 download seata – server – 0.0.9 at the same time, need the configuration files and scripts: https://github.com/seata/seata/releases/tag/v0.9.0

2. Seata service configuration

In the conf directory, back up the configuration file before making any changes

  • Conf, mode Select the database mode and configure the database connection information
  ## store mode: file, db, redis
  mode = "db"
  ## database store propertydb { ## 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" url = "JDBC: mysql: / / 127.0.0.1:3306 / seata" user = "hydra" password = "123456" minConn globalTable = = 5 maxConn = 100 "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 }Copy the code
  • Modify registry.conf to use nacos as the registry and configuration center. You can create a namespace in nacos and copy the values of the generated namespace
  registry {
  #Nacos, Eureka, Redis, ZK, Consul, ETCD3, SOFAType = "nacos" nacos {application = "seata-server" serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "202274f4-218e-42bf-9251-e996df6340f8" cluster = "default" username = "nacos" password = "nacos" } config {  #File, NACOS, Apollo, ZK, Consul, ETCD3Type = "nacos" nacos {serverAddr = "127.0.0.1:8848" namespace = "202274F4-218e-42bf-9251-e996df6340f8" group = "SEATA_GROUP" username = "nacos" password = "nacos" }Copy the code
3. Import Seate parameter configuration to the NACOS configuration center

TXT and nacos-config.sh scripts from SEata server-0.9 to seata/conf 1.4. We need to import the nacos-config. TXT parameters to the NACOS configuration center through the script nacos-config.sh, and then the microservice project will read the configuration from the NACOS configuration center. This eliminates the need to copy the two configuration files to the Resourcce directory of the microservice project as in the previous version.

Nacos-config. TXT, first modify the database configuration:

store.mode=db
store.db.url=JDBC: mysql: / / 127.0.0.1:3306 / seata? useUnicode=true
store.db.user=hydra
store.db.password=123456
Copy the code

In 0.9, the parameter format is still horizontal. In 1.4, the specification is changed, and the horizontal line needs to be changed to the hump. The parameters that need to be changed are:

store.db.db-type=mysql
store.db.driver-class-name=com.mysql.jdbc.Driver
Copy the code

Needs to be changed to a hump:

store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
Copy the code

Also, if you are using mysql8.0 or later, you need to change the driver name.

The specific meaning of the other parameters can view the official document: https://seata.io/zh-cn/docs/user/configurations.html, and modified in accordance with the above rules. Note that the parameters are: service.vgroup_mapping

service.vgroup_mapping.my_test_tx_group=default
Copy the code

It is officially interpreted as a transaction group, but it is not clear how many transaction groups are used. However, by looking at the documentation and some open source projects, it is found that most of them set the key value to the service name of the server side and add as many lines as there are microservices. Two microservices will be used as examples in the next demo, so add:

service.vgroupMapping.order-service-group=default
service.vgroupMapping.stock-service-group=default
Copy the code

Run the nacos-config.sh script using gitbash, taking the IP of nacos:

Sh nacos - config. Sh 127.0.0.1Copy the code

By default, the parameters are stored in the public namespace of nacos Config. You can create a seata namespace in nacos and copy all the parameters to it.

4, build table

Create tables branch_TABLE, global_table, and LOCK_table in the SEATA database, and create table undo_log in the service database for rollback. These scripts can also be found directly in seata-server-0.9.

-- the table to store GlobalSession data
drop table if exists `global_table`;
create table `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`)
);
 
-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table` (
  `branch_id` bigint not null,
  `xid` varchar(128) not null,
  `transaction_id` bigint ,
  `resource_group_id` varchar(32),
  `resource_id` varchar(256) ,
  `lock_key` varchar(128) ,
  `branch_type` varchar(8) ,
  `status` tinyint,
  `client_id` varchar(64),
  `application_data` varchar(2000),
  `gmt_create` datetime,
  `gmt_modified` datetime,
  primary key (`branch_id`),
  key `idx_xid` (`xid`)
);
 
-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table` (
  `row_key` varchar(128) not null,
  `xid` varchar(96),
  `transaction_id` long ,
  `branch_id` long,
  `resource_id` varchar(256) ,
  `table_name` varchar(32) ,
  `pk` varchar(36) ,
  `gmt_create` datetime ,
  `gmt_modified` datetime,
  primary key(`row_key`)
);
 
-- 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 for AT mode XID records. Irrelevant to server (note: business database)
Ux_undo_log is added to 0.3.0+
drop 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;
Copy the code
5, start,

After starting nacos-server, click seata/bin/seata-server.bat to start Seata.

6. Micro-service transformation
  • Introducing SeATA dependencies in microservices:
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>
Copy the code

Druid 1.1.12 is introduced in Seata-spring-boot-starter 1.3.0. If the microservice uses druID connection pools, you can remove the existing DruID dependencies.

  • Modify each microservice yML, mainly to configure nacos and seata, tx-service-group is the key of service. VgroupMapping in nacos-config.txt, we use the microservice name and group suffix here
server:
  port: 8763

spring:
  application:
    name: order-service
  cloud:
    nacos:
      server-addr: 127.0. 01.: 8848
  datasource:
    url: jdbc:mysql://localhost:3306/tenant? useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: hydra
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 8
      min-idle: 1
      max-active: 10
      max-wait: 60000

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: ${spring.application.name}-group
  enable-auto-data-source-proxy: true
  config:
    type: nacos
    nacos:
      server-addr: 127.0. 01.: 8848
      namespace: 202274f4-218e-42bf-9251-e996df6340f8
      group: SEATA_GROUP
      username: nacos
      password: nacos
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0. 01.: 8848
      namespace: 202274f4-218e-42bf-9251-e996df6340f8
      group: SEATA_GROUP
      username: nacos
      password: nacos
# service:
# vgroupMapping:
# order-service-group: default

mybatis-plus:
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.cn.nacos.consumer.entity
Copy the code
  • Configure the seata database agent using MyBatis – Plus as follows:
@Configuration
public class DataSourceProxyConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception {
        // The order service introduced MyBatis -plus, so use the special SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        // Proxy data source
        sqlSessionFactoryBean.setDataSource(new DataSourceProxy(dataSource));
        / / generated SqlSessionFactory
        returnsqlSessionFactoryBean.getObject(); }}Copy the code
  • To invoke the test, OrderService calls StockService, for example, adding to the called service method@Transactionalannotations

StockService provides interfaces:

@Service
public class StockService {
    @Autowired
    private StockMapper stockMapper;

    @Transactional
    public String reduce(a){
        System.out.println("Destocking");

        Stock stock = stockMapper.selectOne(new LambdaQueryWrapper<Stock>().eq(Stock::getId, 1));
        System.out.println(stock.toString());

        stock.setQuantity(stock.getQuantity()-1);
        int result = stockMapper.updateById(stock);
        System.out.println("update result: "+result);

        if (result==1) {throw new RuntimeException("Exception test, ready for rollBack");
        }
        return "stock reduce success"; }}Copy the code

OrderService calls StockService’s service using FeignClient to call StockService and adds the @GlobalTransactional annotation to the method that initiates the transaction:

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private StockClient stockClient;

    @GlobalTransactional
    public String buy(a){
        Order order=new Order();
        order.setId(1L).setMoney(20D);
        int result = orderMapper.insert(order);

        if (result==1){
            System.out.println("Insert order successfully");
        }
        returnstockClient.reduce(); }}Copy the code

The interface of the OrderService is called, an exception is thrown in StockService, and the local transaction in the Reduce method is rolled back first. Select * from ‘order’ where ‘order’ = ‘order’;

In the above log, the xID of the global transaction, the branchId of the branch, and the mode used by Seata are printed, and the rollback status is shown as rollback completed after the two-phase commit with AT mode is completed. Check the und_log table in the service database. Rollback records have been inserted:

In this way, distributed transactions are implemented in the default AT mode in Seata. In this mode, most business scenarios can be dealt with, and basically no business intrusion can be achieved, for programmers, only need to add annotations, do not need to do other business function transformation, you can realize the solution of distributed transactions in an unaware way.

If the article is helpful to you, welcome to pay attention to the public number code nongshen