Seata source code analysis: a preliminary official example

 

 

Quick start

Let's start with a microservice example. The official example uses the AT mode in seata distributed transaction, that is, the example is based on:

  • Based on a relational database that supports local ACID transactions.
  • Java application, accessing database through JDBC..

Use case

Business logic for users to purchase goods. The whole business logic is supported by three micro services:

  • Warehousing service: deduct the warehousing quantity for a given commodity.
  • Order service: create orders according to purchase requirements.
  • Account service: deduct the balance from the user account.

Architecture diagram

Warehousing service

public interface StorageService {

    /**
     * Deduct storage quantity
     */
    void deduct(String commodityCode, int count);
}

Order service

public interface OrderService {

    /**
     * Create order
     */
    Order create(String userId, String commodityCode, int orderCount);
}

Account services

public interface AccountService {

    /**
     * Lending from user account
     */
    void debit(String userId, int money);
}

Main business logic

public class BusinessServiceImpl implements BusinessService {

    private StorageService storageService;

    private OrderService orderService;

    /**
     * purchase
     */
    public void purchase(String userId, String commodityCode, int orderCount) {

        storageService.deduct(commodityCode, orderCount);

        orderService.create(userId, commodityCode, orderCount);
    }
}

public class OrderServiceImpl implements OrderService {

    private OrderDAO orderDAO;

    private AccountService accountService;

    public Order create(String userId, String commodityCode, int orderCount) {

        int orderMoney = calculate(commodityCode, orderCount);

        accountService.debit(userId, orderMoney);

        Order order = new Order();
        order.userId = userId;
        order.commodityCode = commodityCode;
        order.count = orderCount;
        order.money = orderMoney;

        // INSERT INTO orders ...
        return orderDAO.insert(order);
    }
}

SEATA's distributed transaction solution

We only need to use a @ GlobalTransactional annotation to realize the distributed transaction function on the business method.

Examples supported by Dubbo + SEATA

Step 1: establish database

  • Requirement: MySQL with InnoDB engine.

Note: in fact, in the example use case, these three services should have three databases. However, for simplicity, we can only create one database and configure three data sources.

Modify the Spring XML using the database URL/username/password you just created.  

dubbo-account-service.xml dubbo-order-service.xml dubbo-storage-service.xml
        <property name="url" value="jdbc:mysql://x.x.x.x:3306/xxx" />
        <property name="username" value="xxx" />
        <property name="password" value="xxx" />

Step 2: create UNDO_LOG table

SEATA AT mode requires {UNDO_LOG} table

-- Note here 0.3.0+ Add unique index ux_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;

Step 3: create a table for the sample business

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;


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;


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;

Step 4: start the service

There are two main ways to start a service:

First, go to the official website to download the software package and start it with a script

The second is to directly download the seata source code and find the server service.

Let's take the source startup server as an example:

Found server Java this file, run directly.

The default port of the official example service of seata is 8091 and the storage method is file.

Step 5: run the example

Example warehouse: seata-samples

  • Start DubboAccountServiceStarter
  • Start DubboStorageServiceStarter
  • Start DubboOrderServiceStarter
  • Run dubbobusiness tester for demo test

In the dubbobusiness tester example, we let the program sleep for 10 seconds and then throw an exception. During this period, we observe whether the data in the database can be rolled back.

@Override
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
public void purchase(String userId, String commodityCode, int orderCount) {
    LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
    storageService.deduct(commodityCode, orderCount);
    orderService.create(userId, commodityCode, orderCount);

    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    throw new RuntimeException("xxx");

}

 

Keywords: Java Distribution seata

Added by daiwa on Tue, 08 Mar 2022 20:26:34 +0200