[Seata microservice distributed transaction] 2. Construction of Seata microservice distributed transaction project

Keywords: Programming Spring MySQL JDBC Database

Citation dependency

		<!--seata-->
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
			<exclusions>
				<exclusion>
					<artifactId>seata-all</artifactId>
					<groupId>io.seata</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>io.seata</groupId>
			<artifactId>seata-all</artifactId>
			<version>${seata.version}</version>
		</dependency>

Project introduction

  • Order items
  • Deduct inventory items

There are two existing project simulation orders with feign call inventory deduction project.

Order item database:

/*
Navicat MySQL Data Transfer

Source Server         : Local mysql
Source Server Version : 50726
Source Host           : localhost:3306
Source Database       : seata-order

Target Server Type    : MYSQL
Target Server Version : 50726
File Encoding         : 65001

*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for order
-- ----------------------------
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
  `id` int(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary key Id',
  `user_id` int(20) DEFAULT NULL COMMENT 'user Id',
  `pay_money` decimal(11,0) DEFAULT NULL COMMENT 'Payment amount',
  `product_id` int(20) DEFAULT NULL COMMENT 'commodity Id',
  `status` int(11) DEFAULT NULL COMMENT 'state',
  `count` int(11) DEFAULT NULL COMMENT 'Quantity of commodities',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=291 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC COMMENT='Order form';


-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `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 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of undo_log
-- ----------------------------

Inventory item database:

/*
Navicat MySQL Data Transfer

Source Server         : Local mysql
Source Server Version : 50726
Source Host           : localhost:3306
Source Database       : seata-product

Target Server Type    : MYSQL
Target Server Version : 50726
File Encoding         : 65001
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` int(20) NOT NULL COMMENT 'Primary key',
  `product_id` int(11) DEFAULT NULL COMMENT 'commodity Id',
  `price` decimal(11,0) DEFAULT NULL COMMENT 'Price',
  `count` int(11) DEFAULT NULL COMMENT 'Inventory quantity',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC COMMENT='Warehousing service';

-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES ('1', '1', '50', '93');
INSERT INTO `product` VALUES ('2', '2', '30', '99');

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `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 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of undo_log
-- ----------------------------

Build inventory deduction items

Citation dependency

Rely on the top of the article

Write notes

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

package com.tuling;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Tulingvip06MsAlibabaStoreApplication {

	public static void main(String[] args) {
		SpringApplication.run(Tulingvip06MsAlibabaStoreApplication.class, args);
	}

}

Write configuration add agent data source configuration

package com.tuling.seata.config;

import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;


@Configuration
@MapperScan(basePackages = {"com.tuling.seata.mapper"})
public class MyBatisConfig {


    /**
     * Get the attribute structure datasource from the configuration file, pay attention to the prefix. Here is druid. Configure according to your own situation,
     * The prefix of native datasource is "spring.datasource"
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public DataSource hikariDataSource() {
        return new HikariDataSource();
    }

    /**
     * Construct datasource proxy object and replace the original datasource
     *
     * @param hikariDataSource
     * @return
     */
    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSourceProxy(DataSource hikariDataSource) {
        return new DataSourceProxy(hikariDataSource);
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:/mybatis/mapper/**/*.xml"));
        sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:/mybatis/mybatis-config.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage("com.tuling.seata.domin");
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        return sqlSessionFactoryBean;
    }

}

Modified configuration

#Name of Registration Center
spring.application.name=nacos-seata-storage-server
server.port=8082
#Database connection
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/seata-product?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.hikari.username=root
spring.datasource.hikari.password=123456
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver

#Thing group
spring.cloud.alibaba.seata.tx-service-group=prex_tx_group

#Address of nacos Registration Center
spring.cloud.nacos.discovery.server-addr=47.112.217.177:8847
logging.level.com.tuling.seata.mapper=debug

Copy configuration from server

Copy the file.conf and registry.conf files from the server to the resources directory. Do not modify anything.

Business layer code

Only the core code, and finally the source code

Impl minus stock code

    @Override
    public boolean reduceCount(Integer productId, Integer amount) throws Exception {
        log.info("commodity Id:{},Quantity of commodities:{}",productId,amount);
        log.info("current XID: {}", RootContext.getXID());
        // Inventory inspection
        checkStock(productId, amount);
        log.info("Start deduction {} Stock", productId);
        Integer record = productMapper.reduceCount(productId,amount);

        log.info("Closing deduction {} Inventory results:{}", productId, record > 0 ? "Successful operation" : "Inventory deduction failed");
        return false;
    }

Build order items

Citation dependency

Same as inventory deduction

Write notes

Same as inventory deduction

Write configuration add agent data source configuration

Same as inventory deduction

Copy configuration from server

Same as inventory deduction

Project configuration

spring.application.name=nacos-seata-order-server
server.port=8081


spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=60000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/seata-order?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.hikari.username=root
spring.datasource.hikari.password=123456
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver

spring.cloud.alibaba.seata.tx-service-group=prex_tx_group


spring.cloud.nacos.discovery.server-addr=47.112.217.177:8847
logging.level.com.tuling.seata.mapper=debug

ribbon.eager-load.enabled=true

feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000

Core business code

The microservice initiator needs to write a global transaction annotation (in this case, the order service is the initiator). The parameter value of name should not be duplicate

//@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)

    //@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)
    @Transactional
    @Override
    public void createOrder(Order order) {
        log.info("current XID: {}", RootContext.getXID());
        log.info("Order begins,user:{},commodity:{},Number:{},Amount of money:{}", order.getUserId(), order.getProductId(), order.getCount(), order.getPayMoney());
        //Create order
        order.setStatus(0);
        orderMapper.saveOrder(order);
        log.info("Save order{}", order);

        //Remote call inventory service deduct inventory
        log.info("Start of inventory deduction");

        remoteStorageService.reduceCount(order.getProductId(), order.getCount());

        log.info("End of deducted inventory");

/*        //Remote call account service deduction balance
        log.info("Deduction balance start ");
        remoteAccountService.reduceBalance(order.getUserId(), order.getPayMoney());
        log.info("End of deduction balance);*/

        //Make an exception
        List<String> l = new ArrayList<>();
        l.get(100);
        //Modify order status to completed
        log.info("Modify order status start");
        orderMapper.updateOrderStatusById(order.getId(),1);
        log.info("Modify order status end");

        log.info("End of order");
    }

Here, we use spring's common things Management @ Transactional to test the effect and add an exception code to the code.

Start project test

Original data:

Access test: http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50

The exception causes the order data to be rolled back, but the inventory has been reduced.

Now remove the @ GlobalTransactional annotation of the order item and restart the order item

@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)

Access test: http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50

As a result, the order was not added and the inventory was not reduced.

Remove the order item exception code and restart the order item

Access test: http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50

 

Source: https://gitee.com/hekang_admin/tulingvip06-ms-alibaba-nacos-seata.git

Posted by paul2463 on Sat, 04 Apr 2020 03:27:30 -0700