SpringBoot + Dubbo + Seata distributed transaction combat

Foreword

Seata Alibaba is distributed transaction middleware, open source, in an efficient and business 0-invasive manner, to solve the problems facing distributed transaction under micro-services scenario.

In fact, the official GitHubhas been given under a variety of environmental Seataapplications sample project, address: https://github.com/seata/seata-samples.

Why do I want to re-write it again, for two main reasons:

  • Official website code example, rely too much confusion as to which what role
  • Seata less relevant information, the author in the process of building, encountered some pit, record it

First, prepare the environment

This article relates to the software environment is as follows:

  • SpringBoot 2.1.6.RELEASE
  • Dubbo 2.7.1
  • Mybatis 3.5.1
  • 0.6.1 Seat
  • Zookeeper 3.4.10

1, business scene

To simplify the process, we only need two orders and inventory services. When creating an order, call the service inventory, inventory deductions.

Tables involved in the design 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;
复制代码

It also requires a rollback log table:

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;
复制代码

2, Seata download and install

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

After downloading unzip to seata-server-0.6.1\distribution\binthe directory you can see seata-server.bat和seata-server.sh, choose a double-click execution.

Not surprisingly, then, when you see -Server started ...other words, it started normally.

3, Maven relies

Because the project is Dubbo, Dubbo we first introduced its 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>
复制代码

Dubbo service to be registered to Zookeeper, introduced curator client.

<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>
	<version>2.13.0</version>
</dependency>
复制代码

Finally, the introduction of Seata.

<dependency>
	<groupId>io.seata</groupId>
	<artifactId>seata-all</artifactId>
	<version>0.6.1</version>
</dependency>
复制代码

Of course, there are other such Mybatis、mysql-connectorlike on a non-stick, it can be self-introduction.

Second, the project configuration

1、application.properties

Here only you need to configure the database connection information and information related to Dubbo.

server.port=8011

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata
spring.datasource.username=root
spring.datasource.password=root

dubbo.application.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
复制代码

2, the data source

Seata transaction branch is achieved through a proxy data source, so you need to configure a proxy data source, otherwise the transaction will not be rolled back.

@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
	return new DataSourceProxy(dataSource);
}
复制代码

Note that, where the DataSourceProxyclass is located io.seata.rm.datasourceinside the package.

3, Seata configuration

Also you need to configure a global transaction scanner. There are two parameters, one is the application name, is a group transactions.

@Bean
public GlobalTransactionScanner globalTransactionScanner() {
	return new GlobalTransactionScanner("springboot-order", "my_test_tx_group");
}
复制代码

In fact, a series of initial work on Seata transactions are done here.

4, configuration registry

SeataWhen connected to the server requires some configuration items, this time there is a registry.conffile you can specify what registry and configuration files yes.

There are many optional, for example file、nacos 、apollo、zk、consul.

The back four is a mature industry configuration registry product, why there is a file it?

The official intention is not to rely on third parties to configure rapid integration testing on the basis of registry seatafunction, but the filetype itself does not have the dynamic discovery and dynamic configuration capabilities registry.

registry.confDocument reads as follows:

registry {
  type = "file"
  file {
    name = "file.conf"
  }
}
config {
  # file、nacos 、apollo、zk、consul
  type = "file"
  file {
    name = "file.conf"
  }
}
复制代码

If you choose the filetype, the name attribute specifies file.conf, this file is specified in the configuration information of the client or server. For example the transport protocol, server address.

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
}
复制代码

Third, the business code

1. Inventory Service

In the inventory service, to get the commodity code and the total number of purchase, the deduction can be.

<update id="decreaseStorage">
	update t_storage set count = count-${count} where commodity_code = #{commodityCode}
</update>
复制代码

Dubbo will then deduct inventory service interfaces to expose.

2, order service

In order service, the first deduct inventory, then create an order. Finally, an exception is thrown, then go to the database to check whether the transaction is rolled back.

@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {

	System.out.println("开始全局事务。XID="+RootContext.getXID());
	StorageDTO storageDTO = new StorageDTO();
	storageDTO.setCount(orderDTO.getCount());
	storageDTO.setCommodityCode(orderDTO.getCommodityCode());
	
	//1、扣减库存
	storageDubboService.decreaseStorage(storageDTO);
	
	//2、创建订单
	orderDTO.setId(order_id.incrementAndGet());
	orderDTO.setOrderNo(UUID.randomUUID().toString());
	Order order = new Order();
	BeanUtils.copyProperties(orderDTO,order);
	orderMapper.createOrder(order);

	throw new RuntimeException("分布式事务异常..."+orderDTO.getOrderNo());
}
复制代码

It is noteworthy that, in the beginning of the transaction order service method, need to mark @GlobalTransactional. In addition, the service's inventory method, do not need this comment, the transaction will be spread through Dubbo.

IV Notes

1, data source

Remember, Seata transaction branch is achieved through a proxy data source, you must not forget to configure the data source agency.

2, the primary key increment

In the database, the table is the primary key ID from the growing fields. If your field is not incremented, then in the Mybatis insert SQL, the column name you want to write complete.

For example, we can write SQL:

INSERT INTO table_name VALUES (值1, 值2,....)
复制代码

So this time it should be written as:

INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
复制代码

3, serialization problem

In the order, the amountfield type double. In the seata0.6.1release, the default serialization way fastjson, but it will be serialized into this field bigdecimaltype, can cause back type mismatch.

But in a subsequent seata0.7.0version (not yet released), it has been the default serialization changed to jackson.

But no need to worry, this is generally not a problem. The author is a mistake because the lead pack, which leads to find the problem.

4, the code for this article

Sample code in this https://github.com/taoxun/springboot-dubbo-zookeeper-seataarticle: .

5. Other

Welcomed the timely exchange of questions -

Guess you like

Origin juejin.im/post/5d19ee6af265da1bcd37effa