preface

Seata is an open source distributed transaction middleware of Alibaba, which can solve the distributed transaction problems in micro-service scenarios in an efficient and zero-intrusion way.

In fact, the official in making has introduced a variety of circumstances Seata application example project, address: https://github.com/seata/seata-samples.

There are two main reasons why I wrote it all over again:

  • There are too many dependencies in the code examples from the official website to know what works
  • Seata related information is limited, the author encountered some pits in the process of building, record

First, environmental preparation

The software environment involved in this paper is as follows:

  • SpringBoot 2.1.6. RELEASE
  • Dubbo 2.7.1
  • Mybatis 3.5.1 track of
  • Seata 0.6.1
  • Zookeeper 3.4.10

1. Business scenarios

To simplify the process, we only need two services, order and inventory. When an order is created, the inventory service is invoked to deduct the inventory.

The table design involved is as follows:

CREATE TABLE `t_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(255) DEFAULT NULL,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0', ` amount ` double (14, 2) DEFAULT'0.00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8;

CREATE TABLE `t_storage` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
Copy the code

A rollback log table is also required:

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) 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,
  `context` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8;
Copy the code

2. Seata download and install

Open the https://github.com/seata/seata/releases, the latest version is v0.6.1 at present.

Bat and seata-server.sh can be found in the seata-server-0.6.1\distribution\bin directory. Double-click one of them.

Not surprisingly, when you see -server started… And so on, it starts normally.

3. Maven dependency

Since this is a Dubbo project, we first introduce Dubbo dependent dependencies.

<dependency>
	<groupId>org.apache.dubbo</groupId>
	<artifactId>dubbo</artifactId>
	<version>2.7.1</version>
</dependency>
<dependency>
	<groupId>org.apache.dubbo</groupId>
	<artifactId>dubbo-spring-boot-starter</artifactId>
	<version>2.7.1</version>
</dependency>
Copy the code

Dubbo’s service is registered with Zookeeper, importing a curator client.

< the dependency > < groupId > org. Apache. Curator < / groupId > < artifactId > curator - framework < / artifactId > < version > 2.13.0 < / version > </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> The < version > 2.13.0 < / version > < / dependency >Copy the code

Finally, Seata is introduced.

<dependency> <groupId> IO. Seata </groupId> <artifactId>seata-all</artifactId> <version>0.6.1</version> </dependency>Copy the code

Of course, there are other such as Mybatis, mysql-connector, etc.

Ii. Project configuration

1, the application properties

You only need to configure the database connection information and Dubbo information.

Server port = 8011 spring. The datasource. Url = JDBC: mysql: / / 127.0.0.1:3306 / seata spring. The datasource. The username = root Spring. The datasource. Password = root dubbo. Application. The name = order - service dubbo, registry. Address = zookeeper: / / 127.0.0.1:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=20881 dubbo.consumer.timeout=9999999 dubbo.consumer.check=false
Copy the code

2. Data source

Seata implements transaction branching through proxy data sources, so a proxy for the data source needs to be configured first, otherwise the transaction will not be rolled back.

@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
	return new DataSourceProxy(dataSource);
}
Copy the code

Note that the DataSourceProxy class is in the io.seata.rm. Datasource package.

3. Seata configuration

You also need to configure a global transaction scanner. There are two parameters, one is the application name and the other is the transaction group.

@Bean
public GlobalTransactionScanner globalTransactionScanner() {
	return new GlobalTransactionScanner("springboot-order"."my_test_tx_group");
}
Copy the code

In fact, a series of initializations for Seata transactions are done here.

4. Configure the registry

There is a registry. Conf file that specifies what the registry and configuration files are when Seata needs configuration items to connect to the server.

There are many options like File, NACOS, Apollo, ZK, Consul.

The last four are mature configuration registry products in the industry, why there is a file?

The official intent was to quickly integrate and test seATA functionality without relying on third-party configuration registries, but the file type itself does not have the dynamic discovery and configuration capabilities of a registry.

The registry. Conf file contains the following contents:

registry {
  type = "file"
  file {
    name = "file.conf"
  }
}
config {
  # File, NACOS, Apollo, ZK, Consul
  type = "file"
  file {
    name = "file.conf"}}Copy the code

If you select the file type, specify file.conf via the name attribute, which specifies the client or server configuration information. Such as transport protocol, server address, etc.

service {
  #vgroup->rgroup
  vgroup_mapping.my_test_tx_group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
}
Copy the code

Business code

1. Inventory service

In the inventory service, get the item code and the total number of purchases, and deduct it.

<update id="decreaseStorage">
	update t_storage set count = count-${count} where commodity_code = #{commodityCode}
</update>
Copy the code

The inventory service deduction interface is then exposed with Dubbo.

2. Order service

In the order service, inventory is deducted before an order is created. Finally, an exception is thrown and the database is checked to see if the transaction is rolled back.

@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {

	System.out.println("Start global transaction. XID="+RootContext.getXID()); StorageDTO storageDTO = new StorageDTO(); storageDTO.setCount(orderDTO.getCount()); storageDTO.setCommodityCode(orderDTO.getCommodityCode()); / / 1, deduct inventory storageDubboService. DecreaseStorage (storageDTO); Orderto.setid (orderid.incrementandGet ())); orderDTO.setOrderNo(UUID.randomUUID().toString()); Order order = new Order(); BeanUtils.copyProperties(orderDTO,order); orderMapper.createOrder(order); throw new RuntimeException("Distributed transaction exception..."+orderDTO.getOrderNo());
}
Copy the code

It is worth noting that the @GlobalTransactional annotation is required on the method where the order service transaction starts. In addition, in the inventory service approach, this annotation is not required and the transaction is propagated through Dubbo.

Four, notes

1. Data source

Remember that Seata branches transactions through proxy data sources, so don’t forget to configure the data source proxy.

2. Primary key increment

In a database, the primary key ID field in the table is incremented. If your field is not auto-increment, write the column name completely in Mybatis insert SQL.

For example, we could write SQL like this:

INSERT INTO table_name VALUES (value 1, value 2,....)Copy the code

Then it should be written as:

INSERT INTO table_name (表 1, 表 2,...) VALUES (Value 1, value 2,....)Copy the code

Serialization problems

In the order table, the Amount field is of type double. In seata0.6.1, the default serialization is fastjson, but it serializes this field to bigdecimal, resulting in a type mismatch.

However, in a subsequent seata0.7.0 release (not yet released), the default serialization has been changed to Jackson.

But don’t worry, this problem usually doesn’t arise. I found this problem because I mislead a bag.

4. This article code

The sample code in this article: https://github.com/taoxun/springboot-dubbo-zookeeper-seata.

5, other

Welcome to contact us if you have any questions