This is an automated email from the ASF dual-hosted git repository. jimin pushed a commit to branch docusaurus in repository https://gitbox.apache.org/repos/asf/incubator-seata-website.git
The following commit(s) were added to refs/heads/docusaurus by this push: new 2bd4eb1242 optimize: translate the blog (#868) 2bd4eb1242 is described below commit 2bd4eb1242e79afa8173ae5f7331158ddfbf4bb6 Author: 云清 <33415199+lightclouds...@users.noreply.github.com> AuthorDate: Mon Jul 1 00:01:49 2024 +0800 optimize: translate the blog (#868) --- .../springboot-dubbo-mybatisplus-seata.md | 1439 ++++++++++++++++++++ .../tcc-mode-applicable-scenario-analysis.md | 144 ++ .../tcc-mode-design-principle.md | 99 ++ 3 files changed, 1682 insertions(+) diff --git a/i18n/en/docusaurus-plugin-content-blog/springboot-dubbo-mybatisplus-seata.md b/i18n/en/docusaurus-plugin-content-blog/springboot-dubbo-mybatisplus-seata.md index d3ea38285a..08435d7622 100644 --- a/i18n/en/docusaurus-plugin-content-blog/springboot-dubbo-mybatisplus-seata.md +++ b/i18n/en/docusaurus-plugin-content-blog/springboot-dubbo-mybatisplus-seata.md @@ -5,3 +5,1442 @@ description: This article explains how to build the integration of Seata with Sp author: FUNKYE date: 2019/11/29 --- + +# SpringBoot+Dubbo+MybatisPlus integration Seata distributed transactions + +[Project address](https://gitee.com/itCjb/springboot-dubbo-mybatisplus-seata ) + +This article was written by FUNKYE (Chen Jianbin), Hangzhou, an Internet company main program. + +# Preface + +**Transaction**: Transaction is a reliable independent unit of work composed of a set of operations, the transaction has the characteristics of ACID, namely atomicity, consistency, isolation and persistence. +**Distributed Transaction**: When an operation involves multiple services, multiple databases to collaborate on the completion (such as sub-tables and libraries, business split), multiple services, the local Transaction has been unable to cope with this situation , in order to ensure data consistency, you need to use distributed transactions. +**Seata** : is an open source distributed transaction solution , is committed to providing high performance and ease of use in the microservices architecture of distributed transaction services . +**Purpose of this article** : Nowadays, microservices are becoming more and more popular , and the market can be described as a number of distributed transaction solutions , uneven , more popular to MQ on behalf of the guarantee is the ultimate consistency of the message solution ( consumption confirmation , message lookback , message compensation mechanism , etc.) , and TX-LCN LCN mode to coordinate local transactions to ensure that the transaction unified commit or rollback (has stoppe [...] + +# Preparation + +1. First of all, install mysql, eclipse and other commonly used tools, which does not expand. + +2. visit the seata download centre [address](/unversioned/download/seata-server) we use version 0.9.0 + +3. Download and unzip seata-server. + +## Build the library and table + +1.first we link mysql to create a database named seata, and then run the table building sql, this in the seata-server conf folder db_store.sql is what I need to use the sql. + + ```mysql + /* + Navicat MySQL Data Transfer + Source Server : mysql + Source Server Version : 50721 + Source Host : localhost:3306 + Source Database : seata + Target Server Type : MYSQL + Target Server Version : 50721 + File Encoding : 65001 + Date: 2019-11-23 22:03:18 + */ + + SET FOREIGN_KEY_CHECKS=0; + + -- ---------------------------- + + -- Table structure for branch_table + + -- ---------------------------- + + DROP TABLE IF EXISTS `branch_table`; + CREATE TABLE `branch_table` ( + `branch_id` bigint(20) NOT NULL, `xid` varchar + `xid` varchar(128) NOT NULL, `transaction_id` bigint(20) + `transaction_id` bigint(20) DEFAULT NULL, `resource_group_id + `resource_group_id` varchar(32) DEFAULT NULL, `resource_id` varchar(32) + `resource_id` varchar(256) DEFAULT NULL, `lock_key` varchar(256) + `lock_key` varchar(128) DEFAULT NULL, + `branch_type` varchar(8) DEFAULT NULL, `status` tinyint(8) + `status` tinyint(4) DEFAULT NULL, + `client_id` varchar(64) DEFAULT NULL, `application_data` tinyint(4) + `application_data` varchar(2000) DEFAULT NULL, `gmt_create` tinyint(4) DEFAULT NULL, `gmt_create + `gmt_create` datetime DEFAULT NULL, + `gmt_modified` datetime DEFAULT NULL, `gmt_modified` datetime + PRIMARY KEY (`branch_id`), + KEY `idx_xid` (`xid`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + -- ---------------------------- + + -- Records of branch_table + + -- ---------------------------- + + -- ---------------------------- + + -- Table structure for global_table + + -- ---------------------------- + + DROP TABLE IF EXISTS `global_table`; + CREATE TABLE `global_table` ( + `xid` varchar(128) NOT NULL, `transaction_id` varchar(128) + `transaction_id` bigint(20) DEFAULT NULL, `status` tinyint(20) + `status` tinyint(4) NOT NULL, `application_id` varchar(4) + `application_id` varchar(32) DEFAULT NULL, `transaction_service` bigint(20) + `transaction_service_group` varchar(32) DEFAULT NULL, + `transaction_name` varchar(128) DEFAULT NULL, `timeout` int(11.0) + `timeout` int(11) DEFAULT NULL, `begin_time` big + `begin_time` bigint(20) DEFAULT NULL, `application_data` int(11) + `application_data` varchar(2000) DEFAULT NULL, `gmt_create` bigint(20) + `gmt_create` datetime DEFAULT NULL, `gmt_modify` datetime + `gmt_modified` datetime DEFAULT NULL, `gmt_modified` datetime + PRIMARY KEY (`xid`), + KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_tmt_status + KEY `idx_transaction_id` (`transaction_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + -- ---------------------------- + + -- Records of global_table + + -- ---------------------------- + + -- ---------------------------- + + -- Table structure for lock_table + + -- ---------------------------- + + DROP TABLE IF EXISTS `lock_table`; + CREATE TABLE `lock_table` ( + `row_key` varchar(128) NOT NULL, `xid` varchar(128) + `xid` varchar(96) DEFAULT NULL, + `transaction_id` mediumtext, `branch_id` mediumtext, `transaction_id` mediumtext + `branch_id` mediumtext, + `resource_id` varchar(256) DEFAULT NULL, `table_name` varchar(256) + `table_name` varchar(32) DEFAULT NULL, + `pk` varchar(36) DEFAULT NULL, `gmt_create + `gmt_create` datetime DEFAULT NULL, + `gmt_modified` datetime DEFAULT NULL, `gmt_modified` datetime + PRIMARY KEY (`row_key`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + -- ---------------------------- + + -- Records of lock_table + + -- ---------------------------- + + -- ---------------------------- + + -- 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) + `branch_id` bigint(20) NOT NULL, `xid` varchar + `xid` varchar(100) NOT NULL, + `context` varchar(128) NOT NULL, `rollback_info` bigint(20) + `rollback_info` longblob NOT NULL, `log_status` int + `log_status` int(11) NOT NULL, `log_created` datasheet + `log_created` datetime NOT NULL, `log_modified` longblob NOT NULL, `log_status` int(11) + `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 + ``` + +2. After running the database needed for the above seata, we build the library we need to write the demo, create a database named test, and then execute the following sql code. + + ```mysql + /* + Navicat MySQL Data Transfer + Source Server : mysql + Source Server Version : 50721 + Source Host : localhost:3306 + Source Database : test + Target Server Type : MYSQL + Target Server Version : 50721 + File Encoding : 65001 + Date: 2019-11-23 22:03:24 + */ + + SET FOREIGN_KEY_CHECKS=0; + + -- ---------------------------- + + -- Table structure for test + + -- ---------------------------- + + DROP TABLE IF EXISTS `test`. + CREATE TABLE `test` ( + `id` int(11) NOT NULL AUTO_INCREMENT, `one` varchar(255) DEFATE TABLE ( + `one` varchar(255) DEFAULT NULL, + `two` varchar(255) DEFAULT NULL, `createTime` datetime, `createTime` datetime, `createTime` datetime + `createTime` datetime DEFAULT NULL, `two` varchar(255) DEFAULT NULL, `createTime` datetime + PRIMARY KEY (`id`) + ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; + + -- ---------------------------- + + -- Records of test + + -- ---------------------------- + + INSERT INTO `test` VALUES ('1', '1', '2', '2019-11-23 16:07:34'); + + -- ---------------------------- + + -- 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) + `branch_id` bigint(20) NOT NULL, `xid` varchar + `xid` varchar(100) NOT NULL, + `context` varchar(128) NOT NULL, `rollback_info` bigint(20) + `rollback_info` longblob NOT NULL, `log_status` int + `log_status` int(11) NOT NULL, `log_created` datasheet + `log_created` datetime NOT NULL, `log_modified` longblob NOT NULL, `log_status` int(11) + `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=3 DEFAULT CHARSET=utf8; + + -- ---------------------------- + + -- Records of undo_log + ``` + +3. we find the file inside the seata-server/conf folder and edit it: + +4. again find the db configuration method block, change the method as follows: + +Well, you can go to the bin directory./seata-server.bat run to see the + +# Create a project + +first of all, we use eclipse, of course, you can also use idea and other tools, please run in detail according to the following steps + +1. create a new maven project, and delete the extra folder:<img src="/img/blog/20191129133441.png" alt="20191129133441" style={{ zoom:'150%' }} /> + +2. Open the project's pom.xml and add the following dependency. + +```java +<properties +<webVersion>3.1</webVersion +<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding +<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding +<maven.compiler.source>1.8</maven.compiler.source +<maven.compiler.target>1.8</maven.compiler.target +<HikariCP.version>3.2.0</HikariCP.version +<mybatis-plus-boot-starter.version>3.2.0</mybatis-plus-boot-starter.version> +</properties +<parent> +<groupId>org.springframework.boot</groupId> +<artifactId>spring-boot-starter-parent</artifactId> +<version>2.1.8.RELEASE</version>. +</parent +<dependencies +<dependency> +<groupId>org.apache.curator</groupId> +<artifactId>curator-framework</artifactId +<version>4.2.0</version> +</dependency +<dependency> +<groupId>org.apache.curator</groupId> +<artifactId>curator-recipes</artifactId> +<version>4.2.0</version>. +</dependency +<dependency> +<groupId>org.apache.dubbo</groupId> +<artifactId>dubbo-spring-boot-starter</artifactId> +<version>2.7.4.1</version> +</dependency +<dependency> +<groupId>org.apache.commons</groupId> +<artifactId>commons-lang3</artifactId> +</dependency +<dependency> +<groupId>com.alibaba</groupId +<artifactId>fastjson</artifactId> +<version>1.2.60</version> +</dependency +<! -- <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> +<version>7.0</version> <scope>provided</scope> </dependency> --> +<dependency> +<groupId>io.springfox</groupId> +<artifactId>springfox-swagger2</artifactId> +<version>2.9.2</version>. +</dependency +<dependency> +<groupId>io.springfox</groupId> +<artifactId>springfox-swagger-ui</artifactId> +<version>2.9.2</version>. +</dependency + + <! -- mybatis-plus begin --> + <dependency> + <groupId>com.baomidou</groupId> + <artifactId>mybatis-plus-boot-starter</artifactId> + <version>${mybatis-plus-boot-starter.version}</version> + </dependency + <! -- mybatis-plus end --> + <! -- https://mvnrepository.com/artifact/org.projectlombok/lombok --> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <scope>provided</scope> + </dependency + <dependency> + <groupId>io.seata</groupId> + <artifactId>seata-all</artifactId> + <version>0.9.0.1</version> + </dependency + <! -- Zookeeper --> + <dependency> + <groupId>org.apache.zookeeper</groupId> + <artifactId>zookeeper</artifactId> + <version>3.4.9</version> + <exclusions + <exclusion + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion + </exclusions + </dependency + <! -- <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</ artifactId> + <version>2.5.4</version> </dependency> --> + + <! -- <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> + <version>3.1.0</version> </dependency> --> + <! -- https://mvnrepository.com/artifact/org.freemarker/freemarker --> + <dependency> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + </dependency + <! -- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>druid-spring-boot-starter</artifactId> + <version>1.1.20</version> + </dependency + <! -- Add this to recognise the log4j2.yml file --> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-yaml</artifactId> + </dependency + <dependency> <! -- Introducing the log4j2 dependency --> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-log4j2</artifactId> + </dependency + <! -- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + </dependency + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId + <exclusions + <exclusion> + <groupId>org.springframework.boot</groupId + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion + <exclusion + <groupId>org.slf4j</groupId + <artifactId>slf4j-log4j12</artifactId> + </exclusion + </exclusions + </dependency + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId + <scope>test</scope + </dependency + <! -- <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> + <version>2.11.0</version> </dependency> --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-configuration-processor</artifactId> + <optional>true</optional + </dependency + </dependencies + +<optional>true</optional> </dependencies> +``` + +3. and then switch the parent project for pom mode, or pom file, switch to overview , do as shown in the operation: + +4. create our demo sub-project, test-service: + +The directory is as follows. + +<img src="/img/blog/20191129140048.png" alt="20191129140048" style={{ zoom:'200%' }} /> + + Create EmbeddedZooKeeper.java file, along with ProviderApplication.java, with the following code. + +```java +package org.test; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Properties; +import java.util.UUID; + +import org.apache.zookeeper.server.ServerConfig; +import org.apache.zookeeper.server.ZooKeeperServerMain; +import org.apache.zookeeper.server.quorum.QuorumPeerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.SmartLifecycle; +import org.springframework.util.ErrorHandler; +import org.springframework.util.SocketUtils; + +/** + * from: + * https://github.com/spring-projects/spring-xd/blob/v1.3.1.RELEASE/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/zookeeper/ZooKeeperUtils.java + * + * Helper class to start an embedded instance of standalone (non clustered) ZooKeeper. + * + * NOTE: at least an external standalone server (if not an ensemble) are recommended, even for + * {@link org.springframework.xd.dirt.server.singlenode.SingleNodeApplication} + * + * @author Patrick Peralta + * @author Mark Fisher + * @author David Turanski + */ +public class EmbeddedZooKeeper implements SmartLifecycle { + + /** + * Logger. + */ + private static final Logger logger = LoggerFactory.getLogger(EmbeddedZooKeeper.class); + + /** + * ZooKeeper client port. This will be determined dynamically upon startup. + */ + private final int clientPort; + + /** + * Whether to auto-start. Default is true. + */ + private boolean autoStartup = true; + + /** + * Lifecycle phase. Default is 0. + */ + private int phase = 0; + + /** + * Thread for running the ZooKeeper server. + */ + private volatile Thread zkServerThread; + + /** + * ZooKeeper server. + */ + private volatile ZooKeeperServerMain zkServer; + + /** + * {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. + */ + private ErrorHandler errorHandler; + + private boolean daemon = true; + + /** + * Construct an EmbeddedZooKeeper with a random port. + */ + public EmbeddedZooKeeper() { + clientPort = SocketUtils.findAvailableTcpPort(); + } + + /** + * Construct an EmbeddedZooKeeper with the provided port. + * + * @param clientPort + * port for ZooKeeper server to bind to + */ + public EmbeddedZooKeeper(int clientPort, boolean daemon) { + this.clientPort = clientPort; + this.daemon = daemon; + } + + /** + * Returns the port that clients should use to connect to this embedded server. + * + * @return dynamically determined client port + */ + public int getClientPort() { + return this.clientPort; + } + + /** + * Specify whether to start automatically. Default is true. + * + * @param autoStartup + * whether to start automatically + */ + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + /** + * {@inheritDoc} + */ + public boolean isAutoStartup() { + return this.autoStartup; + } + + /** + * Specify the lifecycle phase for the embedded server. + * + * @param phase + * the lifecycle phase + */ + public void setPhase(int phase) { + this.phase = phase; + } + + /** + * {@inheritDoc} + */ + public int getPhase() { + return this.phase; + } + + /** + * {@inheritDoc} + */ + public boolean isRunning() { + return (zkServerThread != null); + } + + /** + * Start the ZooKeeper server in a background thread. + * <p> + * Register an error handler via {@link #setErrorHandler} in order to handle any exceptions thrown during startup or + * execution. + */ + public synchronized void start() { + if (zkServerThread == null) { + zkServerThread = new Thread(new ServerRunnable(), "ZooKeeper Server Starter"); + zkServerThread.setDaemon(daemon); + zkServerThread.start(); + } + } + + /** + * Shutdown the ZooKeeper server. + */ + public synchronized void stop() { + if (zkServerThread != null) { + // The shutdown method is protected...thus this hack to invoke it. + // This will log an exception on shutdown; see + // https://issues.apache.org/jira/browse/ZOOKEEPER-1873 for details. + try { + Method shutdown = ZooKeeperServerMain.class.getDeclaredMethod("shutdown"); + shutdown.setAccessible(true); + shutdown.invoke(zkServer); + } + + catch (Exception e) { + throw new RuntimeException(e); + } + + // It is expected that the thread will exit after + // the server is shutdown; this will block until + // the shutdown is complete. + try { + zkServerThread.join(5000); + zkServerThread = null; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.warn("Interrupted while waiting for embedded ZooKeeper to exit"); + // abandoning zk thread + zkServerThread = null; + } + } + } + + /** + * Stop the server if running and invoke the callback when complete. + */ + public void stop(Runnable callback) { + stop(); + callback.run(); + } + + /** + * Provide an {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. If none + * is provided, only error-level logging will occur. + * + * @param errorHandler + * the {@link ErrorHandler} to be invoked + */ + public void setErrorHandler(ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + + /** + * Runnable implementation that starts the ZooKeeper server. + */ + private class ServerRunnable implements Runnable { + + public void run() { + try { + Properties properties = new Properties(); + File file = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID()); + file.deleteOnExit(); + properties.setProperty("dataDir", file.getAbsolutePath()); + properties.setProperty("clientPort", String.valueOf(clientPort)); + + QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); + quorumPeerConfig.parseProperties(properties); + + zkServer = new ZooKeeperServerMain(); + ServerConfig configuration = new ServerConfig(); + configuration.readFrom(quorumPeerConfig); + + zkServer.runFromConfig(configuration); + } catch (Exception e) { + if (errorHandler != null) { + errorHandler.handleError(e); + } else { + logger.error("Exception running embedded ZooKeeper", e); + } + } + } + } + +} + ``` + + ```java + package org.test; + + import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.apache.dubbo.config.spring.context.annotation. + import org.springframework.boot.SpringApplication; import org.springframework.boot. + import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure. + import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation. + import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation. + + /** + * @author cjbc.annotation.EnableTransactionManagement; /** + * @author cjb + * @date 2019/10/24 + */ + @EnableTransactionManagement. + @ComponentScan(basePackages = {"org.test.config", "org.test.service.impl"}) + @DubboComponentScan(basePackages = "org.test.service.impl") + @SpringBootApplication + public class ProviderApplication { + + public static void main(String[] args) { + new EmbeddedZooKeeper(2181, false).start(); + SpringApplication app = new SpringApplication(ProviderApplication.class); + app.run(args); + } + + } + + ``` + + create entity package org.test.entity and the creation of entity class Test used to lombok, details of Baidu, eclipse installed lombok plug-in + +```java +package org.test.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** +* <p> +* Functions +* </p +* +* @author Funkye +* @since 2019-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@ApiModel(value = "test对象", description = "功能") +public class Test implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "主键") + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @ApiModelProperty(value = "one") + @TableField("one") + private String one; + + @ApiModelProperty(value = "two") + @TableField("two") + private String two; + + @ApiModelProperty(value = "createTime") + @TableField("createTime") + private LocalDateTime createTime; + +} + +``` + +Create service, service.impl, mapper and other packages, in turn create ITestservice, and the implementation class, mapper. + +```java +package org.test.service; + +import org.test.entity.Test; + +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* <p> +* Function Service class +* </p +* +* @author Funkye +* @since 2019-04-10 + */ + public interface ITestService extends IService<Test> { + +} + + ``` + + ```java +import org.apache.dubbo.config.annotation.Service; +import org.test.entity.Test; +import org.test.mapper.TestMapper; +import org.test.service.ITestService; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +@Service(version = "1.0.0",interfaceClass =ITestService.class ) +public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements ITestService { + +} + +``` + +```java + package org.test.mapper; + + import org.test.entity.Test; + + import com.baomidou.mybatisplus.core.mapper.BaseMapper; + + /** + * <p> + * Functional Mapper interface + * </p> + * + * @author Funkye + * @since 2019-04-10 + */ + public interface TestMapper extends BaseMapper<Test> { + + } + + ``` + + Create org.test.config package, create SeataAutoConfig.java, configuration information are here, the main role for the proxy data, connect to the transaction service grouping + +```java +package org.test.config; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import com.alibaba.druid.pool.DruidDataSource; + +import io.seata.rm.datasource.DataSourceProxy; +import io.seata.spring.annotation.GlobalTransactionScanner; + +@Configuration +public class SeataAutoConfig { + @Autowired(required = true) + private DataSourceProperties dataSourceProperties; + private final static Logger logger = LoggerFactory.getLogger(SeataAutoConfig.class); + + @Bean(name = "druidDataSource") + public DataSource druidDataSource() { + DruidDataSource druidDataSource = new DruidDataSource(); + logger.info("dataSourceProperties.getUrl():{}", dataSourceProperties.getUrl()); + druidDataSource.setUrl(dataSourceProperties.getUrl()); + druidDataSource.setUsername(dataSourceProperties.getUsername()); + druidDataSource.setPassword(dataSourceProperties.getPassword()); + druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName()); + druidDataSource.setInitialSize(0); + druidDataSource.setMaxActive(180); + druidDataSource.setMaxWait(60000); + druidDataSource.setMinIdle(0); + druidDataSource.setValidationQuery("Select 1 from DUAL"); + druidDataSource.setTestOnBorrow(false); + druidDataSource.setTestOnReturn(false); + druidDataSource.setTestWhileIdle(true); + druidDataSource.setTimeBetweenEvictionRunsMillis(60000); + druidDataSource.setMinEvictableIdleTimeMillis(25200000); + druidDataSource.setRemoveAbandoned(true); + druidDataSource.setRemoveAbandonedTimeout(1800); + druidDataSource.setLogAbandoned(true); + logger.info("load dataSource........"); + return druidDataSource; + } + + /** + * init datasource proxy + * @Param: druidDataSource datasource bean instance + * @Param: druidDataSource datasource bean instance + * @Return: DataSourceProxy datasource proxy + */ + @Bean(name = "dataSource") + @Primary // In the same DataSource, first use the labelled DataSource + public DataSourceProxy dataSourceProxy(@Qualifier(value = "druidDataSource") DruidDataSource druidDataSource) { + logger.info("Proxy dataSource ........") ; + return new DataSourceProxy(druidDataSource); + } + + /** + * init global transaction scanner + * @Return: GlobalTransactionScanner + * @Return: GlobalTransactionScanner + */ + @Bean + public GlobalTransactionScanner globalTransactionScanner() { + logger.info("Configuring seata........") ; + return new GlobalTransactionScanner("test-service", "test-group"); + } +} + ``` + + Then create the configuration file MybatisPlusConfig, which is required for mybatisplus. + + ```java +package org.test.config; + +import java.util.ArrayList; +import java.util.List; + +import org.mybatis.spring.mapper.MapperScannerConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.baomidou.mybatisplus.core.parser.ISqlParser; +import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser; +import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; + + @Configuration + // @MapperScan("com.baomidou.springboot.mapper*") // This annotation is equivalent to @Bean below. + // MapperScannerConfigurer, 2 configurations of a copy can be + public class MybatisPlusConfig { + + /** + * mybatis-plus paging plugin <br + * Documentation: http://mp.baomidou.com<br> + */ + @Bean + public PaginationInterceptor paginationInterceptor() { + PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); + List<ISqlParser> sqlParserList = new ArrayList<ISqlParser>(); + // Attack the SQL blocking parser and join the parse chain. + sqlParserList.add(new BlockAttackSqlParser()); + paginationInterceptor.setSqlParserList(sqlParserList); + return paginationInterceptor; + } + + /** + * Equivalent to the top: {@code @MapperScan("com.baomidou.springboot.mapper*")} Here it can be extended, e.g., using a configuration file to configure the path to scan the Mapper + */ + + @Bean + public MapperScannerConfigurer mapperScannerConfigurer() { + MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer(); + scannerConfigurer.setBasePackage("org.test.mapper"); + return scannerConfigurer; + } + + } + + ``` + +Create the **resources directory, create the mapper folder, application.yml and other files**. + + ```yaml + server: + port: 38888 + spring: + application: + name: test-service + datasource: + type: com.alibaba.druid.pool.DruidDataSource + url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC + driver-class-name: com.mysql.cj.jdbc.Driver + username: root + password: 123456 + dubbo: + protocol: + loadbalance: leastactive + threadpool: cached + scan: + base-packages: org。test.service + application: + qos-enable: false + name: testserver + registry: + id: my-registry + address: zookeeper://127.0.0.1:2181?client=curator + mybatis-plus: + mapper-locations: classpath:/mapper/*Mapper.xml + typeAliasesPackage: org.test.entity + global-config: + db-config: + field-strategy: not-empty + id-type: auto + db-type: mysql + configuration: + map-underscore-to-camel-case: true + cache-enabled: true + auto-mapping-unknown-column-behavior: none + + ``` + + create file.conf, here the service within the vgroup_mapping. your transaction grouping, for example, on the ** face SeataAutoConfig configured within the test-group, then here should also be changed to test-group **, and then the following ip port are seata running ip and port on the line! + + ```java + transport { + type = "TCP" + server = "NIO" + heartbeat = true + thread-factory { + boss-thread-prefix = "NettyBoss" + worker-thread-prefix = "NettyServerNIOWorker" + server-executor-thread-prefix = "NettyServerBizHandler" + share-boss-worker = false + client-selector-thread-prefix = "NettyClientSelector" + client-selector-thread-size = 1 + client-worker-thread-prefix = "NettyClientWorkerThread" + boss-thread-size = 1 + worker-thread-size = 8 + } + shutdown { + wait = 3 + } + serialization = "seata" + compressor = "none" + } + service { + vgroup_mapping.test-group = "default" + default.grouplist = "127.0.0.1:8091" + enableDegrade = false + disable = false + max.commit.retry.timeout = "-1" + max.rollback.retry.timeout = "-1" + } + + client { + async.commit.buffer.limit = 10000 + lock { + retry.internal = 10 + retry.times = 30 + } + report.retry.count = 5 + tm.commit.retry.count = 1 + tm.rollback.retry.count = 1 + undo.log.table = "undo_log" + } + + recovery { + committing-retry-period = 1000 + asyn-committing-retry-period = 1000 + rollbacking-retry-period = 1000 + timeout-retry-period = 1000 + } + + transaction { + undo.data.validation = true + undo.log.serialization = "jackson" + undo.log.save.days = 7 + undo.log.delete.period = 86400000 + undo.log.table = "undo_log" + } + + metrics { + enabled = false + registry-type = "compact" + exporter-list = "prometheus" + exporter-prometheus-port = 9898 + } + + support { + spring { + datasource.autoproxy = false + } + } + +``` + +Create registry.conf to specify ip ports for file, zk and so on. + +```java +registry { + type = "file" + file { + name = "file.conf" + } +} +config { + type = "file" + file { + name = "file.conf" + } + zk { + serverAddr = "127.0.0.1:2181" + session.timeout = 6000 + connect.timeout = 2000 + } +} + +``` + +Great success, you can directly run it, this time to observe the seata-server + +Next, we create test-client project, here will not repeat, with the above test-service the same way to create + +Next, we copy the test-service service and entities within the past, of course, you are too much trouble, you can get a separate sub-project to put a general service and entities, some tools and so on, I'm here in order to quickly build this demo, the choice of copy and paste the way. + +Directory structure: + + Then we create ClientApplication. + +```java +package org.test; + +import java.util.TimeZone; +import java.util.concurrent.Executor; + +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; + +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, MybatisPlusAutoConfiguration.class}) +@EnableScheduling +@EnableAsync +@Configuration +@EnableDubbo(scanBasePackages = {"org.test.service"}) +@ComponentScan(basePackages = {"org.test.service", "org.test.controller", "org.test.config"}) +public class ClientApplication { + public static void main(String[] args) { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); + SpringApplication app = new SpringApplication(ClientApplication.class); + app.run(args); + } + + @Bean(name = "threadPoolTaskExecutor") + public Executor threadPoolTaskExecutor() { + return new ThreadPoolTaskExecutor(); + } +} + +``` + + Then go to the config package and create SwaggerConfig : + + ```java +package org.test.config; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.service.Parameter; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + + @Configuration + public class SwaggerConfig { + // swagger2 configuration file, here you can configure the swagger2 some basic content, such as scanning packages and so on + @Bean + public Docket createRestApi() { + List<Parameter> pars = new ArrayList<Parameter>(); return new Docket(DocumentationText) + return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select() + // Path to the current package + .apis(RequestHandlerSelectors.basePackage("org.test.controller")).paths(PathSelectors.any()).build() + .globalOperationParameters(pars); + } + + // Build the api document's details function, noting which annotation is referenced here + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + // The title of the page + .title("Project Interface") + // Creator + .contact(new Contact("FUNKYE", "", "")) + // Version number + .version("1.0") + // Description + .description("API description").build(); + } + } + + ``` + +and then create SpringMvcConfigure, and then put inside the configuration of seata, I'm lazy in order to directly integrated in the mvc configuration of the class, you can standardise the point can be created in addition to the configuration of a seata class, you can find the following is still a group name, I have two projects are assigned to a group to go, it seems that another take a also It's okay. + + ```java +package org.test.config; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.dubbo.config.annotation.Reference; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import com.google.common.collect.Maps; + +import io.seata.spring.annotation.GlobalTransactionScanner; + +@Configuration + public class SpringMvcConfigure implements WebMvcConfigurer { + + @Bean + public FilterRegistrationBean corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader(CorsConfiguration.ALL); config.addAllowedHeader(CorsConfiguration.ALL); + config.addAllowedMethod(CorsConfiguration.ALL); config.addAllowedMethod(CorsConfiguration.ALL); + source.registerCorsConfiguration("/**", config); + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new CorsFilter(source)); + filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); + filterRegistrationBean.setOrder(1); + filterRegistrationBean.setEnabled(true); + filterRegistrationBean.addUrlPatterns("/**"); + Map<String, String> initParameters = Maps.newHashMap(); + initParameters.put("excludes", "/favicon.ico,/img/*,/js/*,/css/*"); + initParameters.put("isIncludeRichText", "true"); + filterRegistrationBean.setInitParameters(initParameters); return filterRegistrationBean. + return filterRegistrationBean; } + } + + @Bean + public InternalResourceViewResolver viewResolver() { + InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF") + viewResolver.setPrefix("/WEB-INF/jsp/"); + viewResolver.setSuffix(".jsp"); + // viewResolver.setViewClass(JstlView.class); // This property does not usually need to be configured manually. + // This property does not usually need to be configured manually, as higher versions of Spring will automatically detect it. + return viewResolver; // viewResolver.setViewClass(JstlView.class) + } + + + + /** + * Replacing frame json with fastjson + */ + @Override + public void configureMessageConverters(List<HttpMessageConverter<? >> converters) { + FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); + FastJsonConfig fastJsonConfig = new FastJsonConfig(); + fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, + SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.DisableCircularReferenceDetect); + // Handle garbled Chinese characters + List<MediaType> fastMediaTypes = new ArrayList<>(); + fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); + fastConverter.setSupportedMediaTypes(fastMediaTypes); + fastConverter.setFastJsonConfig(fastJsonConfig); + // Handle strings, avoiding quotes when returning strings directly. + StringHttpMessageConverter smc = new StringHttpMessageConverter(Charset.forName("UTF-8")); + converters.add(smc); + converters.add(fastConverter); + } + + @Bean + public GlobalTransactionScanner globalTransactionScanner() { + return new GlobalTransactionScanner("test-client", "test-group"); } + } + + } + + ``` + +Create the c**ontroller package, and then create the TestController** under the package. + + ```java +package org.test.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.test.service.DemoService; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + /** + * <p> + * Documentation table Front-end controller + * </p + * + * @author funkye + * @since 2019-03-20 + */ + @RestController + @RequestMapping("/test") + @Api(tags = "test interface") + public class TestController { + + private final static Logger logger = LoggerFactory.getLogger(TestController.class); + @Autowired + @Lazy + DemoService demoService; + + @GetMapping(value = "testSeataOne") + @ApiOperation(value = "Test the manual rollback distributed transaction interface") + public Object testSeataOne() { + return demoService.One(); + } + + @GetMapping(value = "testSeataTwo") + @ApiOperation(value = "Test Exception Rollback Distributed Transaction Interface") + public Object testSeataTwo() { + return demoService.Two(); + } + + } + + ``` + +Then go to service and create the demoService you need to depend on. + + ```java +package org.test.service; + +import java.time.LocalDateTime; + +import org.apache.dubbo.config.annotation.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.test.controller.TestController; +import org.test.entity.Test; + +import io.seata.core.context.RootContext; +import io.seata.core.exception.TransactionException; +import io.seata.spring.annotation.GlobalTransactional; +import io.seata.tm.api.GlobalTransactionContext; + +@Service +public class DemoService { + @Reference(version = "1.0.0", timeout = 60000) + private ITestService testService; + private final static Logger logger = LoggerFactory.getLogger(DemoService.class); + + /** + * manual rollback example + * + * @return + */ + @GlobalTransactional + public Object One() { + logger.info("seata distribute transaction Id:{}", RootContext.getXID()); + Test t = new Test(); + t.setOne("1"); + t.setTwo("2"); + t.setCreateTime(LocalDateTime.now()); + testService.save(t); + try { + int i = 1 / 0; + return true; + } catch (Exception e) { + // TODO: handle exception + try { + logger.info("load transaction id for rollback"); + GlobalTransactionContext.reload(RootContext.getXID()).rollback(); + } catch (TransactionException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + return false; + } + + /** + * throw exception and rollback + * + * @return + */ + @GlobalTransactional + public Object Two() { + logger.info("seata分布式事务Id:{}", RootContext.getXID()); + logger.info("seata distribute transaction Id:{}", RootContext.getXID()); + Test t = new Test(); + t.setOne("1"); + t.setTwo("2"); + t.setCreateTime(LocalDateTime.now()); + testService.save(t); + try { + int i = 1 / 0; + return true; + } catch (Exception e) { + // TODO: handle exception + throw new RuntimeException(); + } + } +} + + ``` + +Create the resources folder as usual, starting with the common **application.yml**. + +```java +spring: + application: + name: test + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/test?userSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai + username: root + password: 123456 + mvc: + servlet: + load-on-startup: 1 + http: + encoding: + force: true + charset: utf-8 + enabled: true + multipart: + max-file-size: 10MB + max-request-size: 10MB +dubbo: + registry: + id: my-registry + address: zookeeper://127.0.0.1:2181?client=curator +# address: zookeeper://127.0.0.1:2181?client=curator + application: + name: dubbo-demo-client + qos-enable: false +server: + port: 28888 + max-http-header-size: 8192 + address: 0.0.0.0 + tomcat: + max-http-post-size: 104857600 + +``` + +Copy the service configuration file and registry file, if your client group name is changed in the configuration class, then the group name in the file file needs to be changed as well. + + + +The complete directory structure as above, this time you can start test-service, then start test-client, to swagger test it! + +4. Visit 127.0.0.1:28888/swagger-ui.html to do the final finish  + + + +Here's the data I've saved a record, let's see if we'll successfully rollback: + + + +Refresh the database, found that there is still only one data: + + + +And then check the log. + + + +It shows that it has been rolled back, let's look at the log from seata-server again: + +<img src="/img/blog/20191129143419.png" style={{ zoom:'200%' }} /> + +Display rollback success, transaction id is also consistent, this is our distributed transaction on the run through, through the interruption point way, you can view the undo_log, you will find that before the transaction is committed, will be deposited into a transaction information data, if the rollback is successful, the information will be deleted. + +# Summary + +seata's integration is still relatively simple and easy to start, a little more attentive you must write better than me! + +Welcome to read more seata, dubbo and other source code, can solve the business encountered a lot of pit oh! + diff --git a/i18n/en/docusaurus-plugin-content-blog/tcc-mode-applicable-scenario-analysis.md b/i18n/en/docusaurus-plugin-content-blog/tcc-mode-applicable-scenario-analysis.md index afbf551c02..0343a26dc5 100644 --- a/i18n/en/docusaurus-plugin-content-blog/tcc-mode-applicable-scenario-analysis.md +++ b/i18n/en/docusaurus-plugin-content-blog/tcc-mode-applicable-scenario-analysis.md @@ -4,3 +4,147 @@ author: zhangthen date: 2019/03/27 keywords: [seata, distributed transaction, TCC, roadmap] --- + +# TCC Applicable Model and Applicable Scenario Analysis + +Fescar 0.4.0 version released the TCC model, contributed by the ant gold service team, welcome to try, the end of the article also provides the project follow-up Roadmap, welcome to pay attention. +<a name="2143093f"></a> + +## Preface: Application scenarios based on TCC model + <br /> +<br /> + +The TCC distributed transaction model acts directly on the service layer. It is not coupled with the specific service framework, has nothing to do with the underlying RPC protocol, has nothing to do with the underlying storage media, can flexibly choose the locking granularity of the business resources, reduces the resource locking holding time, has good scalability, and can be said to be designed for independently deployed SOA services. + + +<a name="d9b462de"></a> + +## I. TCC model advantages + +For TCC distributed transaction model, I think its application in business scenarios, there are two aspects of significance. + +<a name="95179108"></a> + +### 1.1 Distributed transaction across services + +The splitting of services can also be thought of as horizontal scaling of resources, only in a different direction. + +Horizontal extensions may go along two directions: + +1. functional scaling, where data is grouped according to function and different functional groups are distributed over multiple different databases, which is effectively servitisation under the SOA architecture. +1. data sharding, which adds a new dimension to horizontal scaling by splitting data across multiple databases within functional groups. + +The following figure briefly illustrates the horizontal data scaling strategy: + + + +Therefore, one of the roles of TCC is to ensure the transaction property of multi-resource access when scaling resources horizontally by function. + +<a name="240e4d15"></a> + +### 1.2 Two-stage splitting + +Another effect of TCC is that it splits the two phases into two separate phases that are related by means of resource business locking. The advantage of resource locking is that it does not block other transactions from continuing to use the same resources in the first phase, nor does it affect the correct execution of the second phase of the transaction. + +**The traditional model of concurrent transactions:**<br /> + + +**Concurrent transactions for the TCC model:**<br /> + + +How does this benefit the business? Taking the secured transaction scenario of Alipay, the simplified case involves only two services, the transaction service and the billing service. The transaction service is the main business service, and the accounting service is the slave business service, which provides the Try, Commit, and Cancel interfaces: + +1. The Try interface deducts the user's available funds and transfers them to pre-frozen funds. Pre-frozen funds is the business locking programme, each transaction can only use the pre-frozen funds of this transaction in the second phase, and other concurrent transactions can continue to process the user's available funds after the first phase of execution. +1. The Commit interface deducts the pre-frozen funds and increases the funds available in the intermediate account (secured transactions do not immediately credit the merchant and require an intermediate account for suspense). + +Assuming there is only one intermediary account, every time the Commit interface of the payment service is called, it locks the intermediary account, and there are hotspot performance issues with the intermediary account. However, in the secured transaction scenario, the funds need to be transferred from the intermediate account to the merchant only after seven days, and the intermediate account does not need to be displayed to the public. Therefore, after executing the first stage of th [...] + + +This is the two-phase asynchronisation feature of TCC distributed transaction model, the first phase of execution from the business service is successful, the master business service can be committed to complete, and then the framework asynchronously execute the second phase of each slave business service. + +<a name="ad5fc026"></a> + +## General-purpose TCC solution + +The generic TCC solution is the most typical implementation of the TCC distributed transaction model, where all the slave business services need to participate in the decision making of the master business service. <br /> +<br /> +<a name="62b37e99"></a> + +### Applicable scenarios + +Since the slave business services are invoked synchronously and their results affect the decisions of the master business service, the generic TCC distributed transaction solution is suitable for businesses with deterministic and short execution times, such as the three most core services of an Internet financial enterprise: transaction, payment, and accounting:<br /> +<br /> <br /> When a user initiates a transaction, the transaction service is accessed first to create the transaction order; then the transaction service calls the payment service to create the payment order for the transaction and performs the collection action, and finally, the payment service calls the billing service to record the account flow and bookkeeping. + +In order to ensure that the three services work together to complete a transaction, either succeed or fail at the same time, you can use a general-purpose TCC solution that puts the three services in a distributed transaction, with the transaction as the master service, the payment as the slave service, and the billing as the nested slave service of the payment service, and the atomicity of the transaction is guaranteed by the TCC model. <br /> + + +The Try interface of the payment service creates the payment order, opens a nested distributed transaction, and calls the Try interface of the billing service; the billing service freezes the buyer's funds in the Try interface. After the first stage of the call is completed, the transaction is completed, the local transaction is submitted, and the TCC framework completes the second stage of the distributed transaction from the business service. + +The second stage of the payment service first calls the Confirm interface of the accounting service to deduct the buyer's frozen funds and increase the seller's available funds. After the call is successful, the payment service modifies the payment order to the completed state and completes the payment. + +When both payment and billing service phase 2 are finished, the whole distributed transaction is finished. + +<a name="827f0f82"></a> + +## Asynchronous guaranteed TCC solution + +The direct slave service of the asynchronous assured TCC solution is the reliable messaging service, while the real slave service is decoupled by the messaging service and executed asynchronously as the consumer of the messaging service. <br /> +<br /> <br /> The Reliable Messaging Service needs to provide three interfaces, Try, Confirm, and Cancel. The Try interface pre-sends, and is only responsible for persistently storing the message data; the Confirm interface confirms the sending, and this is when the actual delivery of the message begins The Confirm interface confirms the delivery, which is when the actual delivery of the message begins; and the Cancel interface cancels the delivery and deletes [...] + +The message data of the message service is stored independently and scaled independently, which reduces the coupling between the business service and the messaging system, and achieves the ultimate consistency of the distributed transaction under the premise that the message service is reliable. + +This solution increases the maintenance cost of message service, but since message service implements TCC interface instead of slave business service, slave business service doesn't need any modification and the access cost is very low. + +<a name="62b37e99-1"></a> + +### Application scenario + +Since consuming messages from a business service is an asynchronous process, the execution time is uncertain, which may lead to an increase in the inconsistency time window. Therefore, the Asynchronous Ensured TCC Distributed Transaction Solution is only applicable to some passive businesses that are less sensitive to the final consistency time (the processing result of the slave business service does not affect the decision of the master business service, and only passively receives the [...] +<br /> <br />When a user registers for a membership successfully, an email needs to be sent to the user to tell the user that the registration was successful and to prompt the user to activate the membership. But pay attention to two points: + +1. If the user registration is successful, make sure to send an email to the user; +1. if the user's registration fails, an email must not be sent to the user. + +So again, this requires the membership service and the mail service to ensure atomicity, either both are executed or neither is executed. The difference is that the mail service is only a passive business, it does not affect whether the user can register successfully or not, it only needs to send an email to the user after the user has registered successfully, and the mail service does not need to be involved in the decision making of the activities of the membership service. + +For this kind of business scenario, you can use the asynchronous ensured TCC distributed transaction solution, as follows:<br /> +<br /> <br /> <br /> The reliable messaging service decouples the member and mail services, and the member service and the messaging service comprise the TCC transaction model, which ensures the atomicity of transactions. Then through the reliable feature of the message service, it ensures that the message can definitely be consumed by the mail service, so that the member and the mail service are in the same distributed transaction. At the same time, the mai [...] + +<a name="69910d05"></a> + +## Compensated TCC solution + +Compensated TCC solution is similar in structure to generic TCC solution, and its slave services also need to participate in the decision making of the main business service. However, the difference is that the former slave service only needs to provide Do and Compensate two interfaces, while the latter needs to provide three interfaces. <br /> +<br /> <br /> The Do interface directly executes the real complete business logic, completes the business processing, and the result of the business execution is visible externally; the Compensate operation is used for the business compensation, which offsets or partially offsets the business result of the positive business operation. Compensate operation needs to satisfy idempotency. <br />Compensate operation is used to offset or partially offset the busin [...] + +<a name="62b37e99-2"></a> + +### Applicable scenarios + +Due to the existence of rollback compensation failure, the compensated TCC distributed transaction solution is only applicable to some of the less concurrent conflict or need to interact with external business, these external business is not a passive business, its execution results will affect the decision of the main business service, such as the ticket booking service of the air ticket agency:<br /> +<br /> <br /> This air ticket service provides multi-destination air ticket booking service, which can book air tickets for multiple itinerary flights at the same time, e.g., to travel from Beijing to St. Petersburg, it is necessary to take the first journey from Beijing to Moscow, as well as the second journey from Moscow to St. Petersburg. + +When a user books a ticket, he/she would definitely want to book tickets for both flights at the same time, and booking only one flight does not make sense for the user. Therefore, such a business service also imposes the atomicity requirement that if the booking for one of the flights fails, the other flight needs to be able to be cancelled. + +However, it is extremely difficult to push the airlines to change as they are external to the ticket agents and only provide booking and cancellation interfaces. Therefore, for this type of business service, a compensated TCC distributed transaction solution can be used, as follows:<br /> + + +The gateway service adds the Compensate interface on top of the original logic, which is responsible for calling the cancellation interface of the corresponding airline. + +When the user initiates a ticket booking request, the ticket service first calls the booking interface of each airline through the Do interface of the gateway, and if all flights are booked successfully, the whole distributed transaction is executed successfully; once the booking of tickets for a certain flight fails, the distributed transaction is rolled back, and the Compensate interface of each gateway is called by the TCC transaction framework, which then calls the corresponding airl [...] + +<a name="acfe75a0"></a> + +## V. Summary + +For today's Internet applications, horizontal scaling of resources provides more flexibility and is a relatively easy to implement outward scaling solution, but at the same time, it also significantly increases the complexity and introduces some new challenges, such as data consistency issues between resources. + +Horizontal data scaling can be done both by data slicing and by functionality. the TCC model ensures the transactional properties of multi-resource access while scaling resources horizontally by functionality. + +TCC model in addition to the role of cross-service distributed transactions this layer , but also has a two-stage division of the function , through the business resource locking , allowing the second stage of asynchronous execution , and asynchronous idea is to solve the hot spot data concurrency performance problems of one of the tools . <br /> +<a name="Roadmap"></a> + +## Roadmap + +Currently, we have released 0.4.0, and we will release 0.5 ~ 1.0 to continue to improve and enrich the functionality of AT and TCC modes, and to solve the problem of high availability of the server side. After 1.0, this open source product will reach the standard of production environment. <br /><br /><br /><br /> + diff --git a/i18n/en/docusaurus-plugin-content-blog/tcc-mode-design-principle.md b/i18n/en/docusaurus-plugin-content-blog/tcc-mode-design-principle.md index 09938d3a99..f00359471b 100644 --- a/i18n/en/docusaurus-plugin-content-blog/tcc-mode-design-principle.md +++ b/i18n/en/docusaurus-plugin-content-blog/tcc-mode-design-principle.md @@ -4,3 +4,102 @@ author: zhangthen date: 2019/03/26 keywords: [fescar, distributed transaction, TCC, roadmap] --- + +# Introduction to TCC Theory and Design Implementation Guidelines + +Fescar 0.4.0 version released the TCC schema, contributed by the Anthem team, you are welcome to try it out,<br />Sample address:[https://github.com/fescar-group/fescar-samples/tree/master/tcc](https. //github.com/fescar-group/fescar-samples/tree/master/tcc),<br />At the end of this article, we also provide the roadmap of the project, welcome to follow. + +<a name="f1d2fc6a"></a> + +### I. Introduction to TCC + +In the Two Phase Commitment Protocol (2PC), the resource manager (RM, resource manager) needs to provide three functions: "prepare", "commit" and "rollback". "Rollback" 3 operations; while the transaction manager (TM, transaction manager) coordinates all resource managers in 2 phases, in the first phase asks all resource managers whether the "preparation" is successful, if all resources are If all resources are "ready" successfully, then perform "commit" operation of all resources in the [...] + +Resource Manager has many implementations, among which TCC (Try-Confirm-Cancel) is a service-based implementation of Resource Manager; TCC is a relatively mature distributed transaction solution that can be used to solve the data consistency problem of cross-database and cross-service business operations; TCC's Try, Confirm and Cancel methods are implemented by business code. TCC's Try, Confirm, and Cancel methods are all implemented by business code, so TCC can be called a service-based [...] + +The Try operation of TCC is the first stage, which is responsible for checking and reserving resources; Confirm operation is the second stage, which is the submit operation to execute the real business; Cancel is the second stage, which is the rollback operation, which is the cancellation of the reserved resources to return the resources to the initial state. + +As shown in the figure below, after the user implements a TCC service, the TCC service will be one of the resources of the distributed transaction, participating in the whole distributed transaction; the transaction manager coordinates the TCC services in two stages, calling the Try method of all TCC services in the first stage, and executing the Confirm or Cancel method of all TCC services in the second stage; eventually all TCC services are either committed or cancelled; all TCC servic [...] + + + +<a name="48153343"></a> + +### II. TCC Design + +When users access TCC, most of the work is focused on how to implement TCC service, after years of TCC application by Anthem, the following main TCC design and implementation of the main matters are summarised below: + +<a name="4226dc7c"></a> + +#### 1, **Business operation is completed in two stages** + +Before connecting to TCC, business operation can be completed in one step only, but after connecting to TCC, we need to consider how to divide it into 2 phases to complete, put the resource checking and reserving in Try operation in the first phase, and put the execution of real business operation in Confirm operation in the second phase. + +Below is an example of how the business model can be designed in two phases. Example scenario: "Account A has a balance of $100, of which $30 needs to be deducted"; + +Before accessing TCC, the user could write SQL: "update account table set balance = balance - 30 where account = A" to complete the deduction operation in one step. + +After connecting to TCC, you need to consider how to split the debit operation into 2 steps: + +* Try operation: checking and reserving resources; + +In the deduction scenario, what Try operation has to do is to check whether the balance of A account is enough, and then freeze the $30 to be deducted (reserved resources); no real deduction will happen at this stage. + +* Confirm operation: performs the submission of the real operation; + +In the deduction scenario, the Confirm phase takes place when the real deduction occurs, deducting the $30 already frozen in A's account. + +* Cancel operation: whether or not the reserved resource is released; + +In a debit scenario, the debit is cancelled and the Cancel operation performs the task of releasing the $30 that was frozen by the Try operation, returning Account A to its initial state. + + + + +<a name="bce861f1"></a> + +#### 2, **Concurrency Control** + +Users should consider concurrency issues when implementing TCC and minimise lock granularity to maximise concurrency in distributed transactions. + +The following is still an example of deducting money from account A. "There is $100 on account A. Transaction T1 has to deduct $30 of it, and transaction T2 also has to deduct $30, and there is concurrency". + +In the first phase of the Try operation, distributed transaction T1 and distributed transaction T2 are freezing that part of the funds without interfering with each other; so that in the second phase of the distributed transaction, no matter whether T1 is a commit or a rollback, there will be no impact on T2, so that T1 and T2 are executing in parallel on the same piece of business data. + + <br /> + +<a name="e945e352"></a> + +#### 3, **Allow empty rollback** + +As shown in the following figure, when the transaction coordinator invokes the first-phase Try operation of the TCC service, there may be a network timeout due to packet loss, and at this time the transaction manager triggers a two-phase rollback to invoke the Cancel operation of the TCC service, which is invoked without a timeout. + +The TCC service receives a Cancel request without receiving a Try request, this scenario is called a null rollback; null rollbacks often occur in production environments, and users should allow for null rollbacks when implementing TCC services, i.e., return success when receiving a null rollback. + + + +<a name="e02f3ee9"></a> + +#### 4. Anti-suspension control + +As shown in the figure below, when the transaction coordinator calls the TCC service's one-phase Try operation, there may be a timeout due to network congestion, at this time, the transaction manager will trigger a two-phase rollback and call the TCC service's Cancel operation, and the Cancel call is not timed out; after this, the one-phase Try packet that is congested in the network is received by the TCC service, and there is a two-phase After this, the first-phase Try packet on the co [...] + +When you implement TCC service, you should allow empty rollback, but refuse to execute Try request after empty rollback to avoid hanging. + + + + +<a name="5322a3d5"></a> + +#### 5. Idempotent control + +Whether it is network packet retransmission or compensation execution of abnormal transaction, it will lead to the Try, Confirm or Cancel operation of TCC service to be executed repeatedly; users need to consider idempotent control when implementing TCC service, i.e., the business result of Try, Confirm, Cancel executed once and executed many times is the same. <br /><br /><br /> + +<a name="Roadmap"></a> + +### Roadmap + +Currently we have released version 0.4.0, we will release version 0.5 ~ 1.0, continue to improve and enrich the functions of AT, TCC mode, and solve the problem of high availability of the server side, after version 1.0, this open source product will reach the standard of production environment. + + + --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org For additional commands, e-mail: notifications-h...@seata.apache.org