Analysis of LCN's Solution to Distributed Transaction Principle + Project Actual Combat

Preface

The SpringCloud distributed architecture brings us development convenience, and at the same time increases the difficulty of our transaction management. Microservices are blooming everywhere, and local transactions can no longer meet the requirements of distributed, and thus the distributed transaction problem was born. Distributed transactions are called a worldwide problem.
  For more information on distributed transactions, please see this article: Someone asks you about distributed transactions, and throw this to him.
  This article records the integration of TX-LCN distributed transaction framework to manage distributed transactions. The version used is 5.0.2.RELEASE
Understand
the background of distributed transactions

Before the emergence of distributed, Internet companies’ projects were traditional monolithic projects, and the entire project shared a data source, so when business execution was performed on the database, this kind of transaction inconsistency problem would not occur, because it was the same. One local transaction for one data source.

But with the evolution of our architecture, from monolithic architecture to distributed architecture to SOA architecture projects to the microservice architecture adopted by the company today, we have split the business of the service, and then the data source is also split, generally microservices A project is a service corresponding to a data source.

Then under this architecture, each service has its own independent data source and its own local transaction, which will generate distributed transactions, which may cause data inconsistency between services.
Solve transaction problems under different architecture systems
Monolithic architecture (single data source)

In a single project, multiple different business logics are implemented in the same data source for transaction management. There is no problem of distributed transactions, because the transaction manager is used in the case of the same data source, which is equivalent to each One transaction manager corresponds to one data source
Monolithic architecture (multiple data sources)

In a single project, there are multiple different data sources, and each data source has its own independent transaction manager, which does not affect each other, then there will also be multi-data source transaction management at this time: solution jta + Atomikos.
Distributed/microservice architecture

In the distributed/microservice architecture, each service has its own independent data source and transaction manager. In this case, if there is a business that needs to make RPC remote calls, it will inevitably generate distributed transactions. At present, the main solutions are: MQ, LCN, Seata and other solutions.
Introduction and background of LCN

The LCN distributed transaction framework itself does not create transactions, but is based on the coordination of local transactions to achieve the effect of transaction consistency.
The first version of the LCN framework was released in June 2017. From the beginning of 1.0, it has evolved to version 5.0.

The LCN name is named after the earlier version of the LCN framework. In the 1.0 ~ 2.0 version at the beginning of the design of the framework, the steps of the framework design are as follows, each takes the LCN name derived from its initials.

Since the framework is compatible with LCN, TCC, TXC three transaction modes after 5.0, in order to avoid distinguishing the LCN mode, the LCN distributed transaction is hereby renamed the TX-LCN distributed transaction framework.
TX-LCN distributed transaction framework, LCN does not produce transactions, LCN is just a coordinator of local transactions, LCN is a high-performance distributed transaction framework, compatible with dubbo and springcloud frameworks, supports RPC framework expansion, and supports various ORM frameworks, NoSQL, load balancing, transactions compensation
positioning

TX-LCN is positioned as a transaction coordination framework. The framework itself does not operate transactions, but is based on the coordination of transactions to achieve the effect of transaction consistency. (LCN does not produce affairs, it is just a porter of affairs...woc this is a bit like the copywriting of Nongfu Spring)
LCN mode

The LCN mode is to realize the operation of local transactions through the proxy Connection, and then coordinate and control the transactions by the TxManager. When the local transaction commits and rolls back or closes the connection, a fake operation will be performed, and the agent's connection will be managed by the LCN connection pool.
Features of this mode:

该模式对代码的嵌入性为低。
该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。

TCC mode

Compared with the traditional transaction mechanism (X/Open XA Two-Phase-Commit), the TCC transaction mechanism is characterized in that it does not rely on the support of the resource manager (RM) for XA, but through the business logic (provided by the business system) Scheduling to achieve distributed transactions. Mainly consists of three steps, Try: try to execute the business, Confirm: confirm the execution of the business, Cancel: cancel the execution of the business.

Features of this model:

该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
该模式对有无本地事务控制都可以支持使用面广。
数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。

TXC mode

The TXC mode is named from Taobao. The implementation principle is to query the impact data of SQL before executing SQL, and then save the executed SQL quick walk information and create locks. When a rollback is required, these recorded data are used to roll back the database. The current lock implementation relies on redis distributed lock control.
Features of this model:

该模式同样对代码的嵌入性低。
该模式仅限于对支持SQL方式的模块支持。
该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
该模式不会占用数据库的连接资源。

Principle of LCN distributed transaction (understand by yourself, easy to understand in vernacular)

1) First of all, our lcn coordinator (TM) will maintain a long connection (continuous monitoring) with the lcn client (TC) through the introduced netty.

2) Before the requesting party (caller) enters the interface service, it will enter the @LcnTransaction annotation through AOP technology to generate and register a global transaction group Id (groupId) on the LCN coordinator side.

3) When the initiator (caller) calls the participant (callee) through rpc, lcn rewrites the Feign client, and will get the transaction group Id (groupId) from ThreadLocal and transfer the transaction group Id Set to the request header.

4) When the participant (callee) obtains the groupId in the request header, lcn will identify the service as a participant and join the transaction group, and will be represented by lcn as the data source. When the service business logic is completed After that, the false close of the data source will not actually commit or roll back the current service transaction.

5) When the initiator executes all the business logic, if there is no exception, the lcn coordinator will be notified, and the lcn coordinator will tell all the participants in the request chain that they can submit, and then the real submission will be carried out. If the initiator reports an error after calling the participants, it will also inform the lcn coordinator, and the lcn coordinator will then inform all participants to perform a real rollback operation, which solves the problem of distributed transactions.
Quickly code the code and
build tx-manager
Create database, table

创建MySQL数据库, 名称为:tx-manager(我们直接选择在我们自己的数据库下面创建表就行了,这里就不创建这个数据库)
创建数据表:t_tx_exception

CREATE TABLE `t_tx_exception`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `transaction_state` tinyint(4) NULL DEFAULT NULL,
  `registrar` tinyint(4) NULL DEFAULT NULL,
  `remark` varchar(4096) NULL DEFAULT  NULL,
  `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
  `create_time` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

https://img2018.cnblogs.com/blog/1353055/201906/1353055-20190620151602501-133032900.png
Download the source code and compile

Source code download address: https://github.com/codingapi/tx-lcn
project directory

Modify the configuration file application.properties of txlcn-tm

####################### 服务 ############################################
 
spring.application.name=TransactionManager
server.port=7970
 
####################### 数据库 ############################################
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.92.145.192:3306/scm_transaction?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=db_user2
spring.datasource.password=db_pass
# 验证连接是否有效。此参数必须设置为非空字符串,下面三项设置成true才能生
spring.datasource.validationQuery=SELECT 1
# 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
spring.datasource.testWhileIdle=true
# 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
spring.datasource.testOnBorrow=true
# 指明是否在归还到池中前进行检验
spring.datasource.testOnReturn=false
 
# 以下可省略
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=10
spring.datasource.maxActive=1000
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
#通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000;druid.stat.logSlowSql=true
#合并多个DruidDataSource的监控数据
spring.datasource.useGlobalDataSourceStat=true
#spring.datasource.WebStatFilter.exclusions="*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
#spring.datasource.stat-view-servlet.login-username=admin
#spring.datasource.stat-view-servlet.login-password=admin
 
####################### 数据库方言 ############################################
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# 第一次运行可以设置为: create, 为TM创建持久化数据库表
spring.jpa.hibernate.ddl-auto=update
 
####################### Redis ############################################
spring.redis.host=47.92.145.192
spring.redis.port=6379
spring.redis.password=WZTH@dev123
 
####################### 事务 ############################################
# TM监听IP. 默认为 127.0.0.1
tx-lcn.manager.host=127.0.0.1
# TM监听Socket端口. 默认为 ${server.port} - 100
tx-lcn.manager.port=8070
# 心跳检测时间(ms). 默认为 300000
tx-lcn.manager.heart-time=300000
# 分布式事务执行总时间(ms). 默认为36000
tx-lcn.manager.dtx-time=8000
# 参数延迟删除时间单位ms  默认为dtx-time值
tx-lcn.message.netty.attr-delay-time=${tx-lcn.manager.dtx-time}
# 事务处理并发等级. 默认为机器逻辑核心数5倍
tx-lcn.manager.concurrent-level=160
# TM后台登陆密码,默认值为codingapi
tx-lcn.manager.admin-key=123456
# 分布式事务锁超时时间 默认为-1,当-1时会用tx-lcn.manager.dtx-time的时间
tx-lcn.manager.dtx-lock-time=${tx-lcn.manager.dtx-time}
# 雪花算法的sequence位长度,默认为12位.
tx-lcn.manager.seq-len=12
# 异常回调开关。开启时请制定ex-url
tx-lcn.manager.ex-url-enabled=false
# 事务异常通知(任何http协议地址。未指定协议时,为TM提供内置功能接口)。默认是邮件通知
tx-lcn.manager.ex-url=/provider/email-to/[email protected]

Note: I have modified the database name, user name and password according to my actual situation.
Start the txlcn-tm module

If you don’t know how to start, you can check the Springboot running mode
by yourself. After starting, open the background address http://localhost:7970, the initial password is codingapi, I changed it to 123456 here

Set
up Tx-Client after logging in

TC terminal refers to the official website for step-by-step operation: https://www.txlcn.org/zh-cn/docs/start.html
TC introduces dependencies

<dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-tc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
 
        <dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-txmsg-netty</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

PS: If you do not add the jdbc driver, an error will be reported when starting

Parameter 0 of constructor in com.codingapi.txlcn.tc.core.transaction.txc.analy.TableStructAnalyser required a bean of type ‘javax.sql.DataSource’ that could not be found.

So add jdbc dependency

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

Add the TM address and listening port to the configuration file. If TM is the default port 8070 and deployed on the same machine as the TC, you can ignore this configuration and enable the log. It is best to enable the log during the development phase and set it to the debug level to facilitate tracking Troubleshoot

# 是否启动LCN负载均衡策略(优化选项,开启与否,功能不受影响)
tx-lcn.ribbon.loadbalancer.dtx.enabled=true
# 默认之配置为TM的本机默认端口
tx-lcn.client.manager-address=127.0.0.1:8070
# 开启日志,默认为false
tx-lcn.logger.enabled=true
tx-lcn.logger.driver-class-name=${spring.datasource.driver-class-name}
tx-lcn.logger.jdbc-url=${spring.datasource.url}
tx-lcn.logger.username=${spring.datasource.username}
tx-lcn.logger.password=${spring.datasource.password}
logging.level.com.codingapi.txlcn=DEBUG

Use @EnableDistributedTransaction on the startup class

//省略其他代码...
@EnableDistributedTransaction
public class MyspringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyspringbootApplication.class, args);
    }
}

Add @LcnTransaction to the place where the local transaction is submitted, the distributed transaction annotation, PS: The target of @LcnTransaction is on the method, @Target({ElementType.METHOD})
demonstrates the demo

We select the two previous projects myspringboot and springdatejpa, follow the steps to set them to TC,
and add the test interface
myspringboot-controller to the two TCs

/**
     * 测试分布式事务
     */
    @GetMapping("feign/save")
    Result<UserVo> save(UserVo userVo){
        //模拟数据
        Description description = new Description();
        description.setUserId("111");
        description.setDescription("测试用户描述");
 
        Result<Description> save = descriptionService.save(description);
        System.out.println(save);
        return null;
    }

myspringboot-service

@Override
    @LcnTransaction//分布式事务
    @Transactional //本地事务
    public Result<Description> save(Description description) {
        UserVo userVo = new UserVo();
        userVo.setUsername("huanzi");
        userVo.setPassword("123");
        //调用springdatejpa服务保存userVo
        Result<UserVo>  result = myspringbootFeign.save(userVo);
        System.out.println(result);
 
        //myspringboot本地服务保存description
        Description save = descriptionRepository.save(description);
        System.out.println(save);
        
        //模拟发生异常
        throw new RuntimeException("business code error");
    }

myspringboot-feign

@FeignClient(name = "springdatejpa", path = "/user/",fallback = MyspringbootFeignFallback.class,fallbackFactory = MyspringbootFeignFallbackFactory.class)
public interface MyspringbootFeign {
 
    @RequestMapping(value = "save")
    Result<UserVo> save(@RequestBody UserVo userVo);
}

springdatejpa

This originally has a corresponding save interface, we will not post the other codes, rewrite the save method in the UserServiceImpl class, and add the @LcnTransaction annotation to the save method

@LcnTransaction//分布式事务
    @Transactional //本地事务
    @Override
    public Result<UserVo> save(UserVo entity) {
        User user = userRepository.save(FastCopy.copy(entity, User.class));
        return Result.of(FastCopy.copy(user, UserVo.class));
    }

Demonstration effect

Start all projects, remember to start TM and Redis services

Check the TM background, you can see that two TCs have been successfully registered

Visit http://localhost:10010/myspringboot/feign/save, it is intercepted by single sign-on, and jump to the interface normally after logging in. These will not be demonstrated anymore. Let’s look directly at the background debug log.
Calling process
myspringboot (A) — > springdatejpa (B)

Transaction rollback
myspringboot (A)

springdatejpa(B)

At this point, springdatejpa (B) has responded to user data to myspringboot (A), and then received a rollback notification

Transaction submission
Let's take a look at how the transaction is normally submitted. We annotate the simulated exception and return the saved data

//模拟发生异常
        //throw new RuntimeException("business code error");
        return Result.of(save);

We look at myspringboot(A) directly after responding to the data from springdatejpa(B)


postscript of springdatejpa(B)

To pay attention to whether our springboot version is compatible with the txlcn version, follow the quick start on the official website (https://www.txlcn.org/zh-cn/docs/start.html) and refer to the official example (https:// github.com/codingapi/txlcn-demo), I encountered a few small problems along the way, here is a summary:

1. A adjusts B, A throws an exception, A transaction is rolled back, B transaction is not rolled back

Reason: This is because I called B at the controller layer of A at the beginning, which is equivalent to that B is a separate transaction group, and A is a separate transaction group

Solution: Call B after A opens the transaction
——————————————
Copyright statement: This article is the original article of the CSDN blogger "Twilight de Baixueyan", following CC 4.0 BY- SA copyright agreement, please attach the original source link and this statement for reprinting.
Original link: https://blog.csdn.net/lucky_love816/article/details/108396683

Guess you like

Origin blog.csdn.net/qq_34117294/article/details/114986818