专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

TX-LCN分布式事务实践

一、需求

扣减库存服务和生成订单服务对应不同数据库,Spring本地事务@Transactional并不能解决跨库跨服务保证数据一致性。分布式事务一般包含事务的发起者和参与者、关系型数据库资源服务以及事务管理器;Distributed Transaction Framework流行的主要有TX-LCN和阿里的seata框架,下一篇会调研下seata框架。

TX-LCN分布式事务框架是一款开源分布式事务框架,由两大模块组成TxClient和TxManager,TxClient扮演发起者和参与者,TxManager扮演事务管理器协调事务;从开发角度讲,TxClient指的是是我们自己的服务系统,TxManager是事务中心的协调系统;从Github RELEASE版本看,最新是5.0.2.RELEASE(支持LCN TXC TCC 三种事务模式),项目为了稳定使用v4.1.0(默认只支持LCN模式);LCN模式基本原理是代理切面拦截所有数据库链接的提交和回滚,由代理连接对象控制本地事务的真正的提交、回滚和释放。若是存在与非关系型数据库redis,就需要TCC模式补偿操作,来保证非关系redis和关系mysql整体一致性。

二、开始准备

1、准备mysql和redis环境,通过spring initializr快速准备eureka注册中心。

2、下载[v4.1.0][]版本,tx-lcn-4.1.0是spring boot项目需要eureka注册中心服务和redis,运行启动类com.codingapi.tm.TxManagerApplication。

3、访问http://127.0.0.1:8899/TxManager管理界面,注意两个属性负载均衡服务器地址的端口和当前连接数,这是实验成功的截图,一开始当前连接数应该是0。

52_1.png

4、springcloud LCN分布式事务v4.0 示例demo

根据教程引导创建相应的数据库和修改配置,重点标注重要配置。我们重点关系jdbc版本的springcloud-jdbc-demo,它涉及到了5个业务模块,工程从1到5对应端口port:8081到8085,控制器的接口前缀是localhost:port/demo,分list列表接口和save接口;在save方法中,在demo3(调用4和5,自己)和demo1(调用2和3,自己)是事务发起方,两者差别是demo3注释了异常能正常返回insert 3条数据,而demo1打开异常触发分布式事务回滚insert数据;demo2、demo4和demo5仅仅是事务参与方。

feign.hystrix.enabled=false

spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url= jdbc:mysql://localhost:3306/test
spring.datasource.username= root
spring.datasource.password=root
spring.datasource.initialize =  true
init-db= true

spring.application.name = demo3
server.port = 8083
#${random.int[9000,9999]},注册中心端口要对应
eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/

feign.hystrix.enabled=true

# 关于**springcloud-hystrix机制,选择信号量隔离** http://www.jianshu.com/p/b8d21248c9b1
hystrix.command.default.execution.isolation.strategy= SEMAPHORE
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000

#Ribbon的负载均衡策略,重试次数为0
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
ribbon.MaxAutoRetriesNextServer=0

#**txmanager地址端口指的是TxManager管理界面的负载均衡服务器地址的端口**
tm.manager.url=http://127.0.0.1:8899/tx/manager/

logging.level.com.codingapi=debug

以demo1的DemoServiceImpl异常触发分布式为例,重点是@TxTransaction(isStart = true)标注事务发起方,否则ThreadLocal不会有groupid,那就不会有事务组,更不可能实现回滚事务。

未标记发起方出现异常则groupId为空情况:

2020-05-30 17:30:06.387 DEBUG 4964 --- [nio-8084-exec-8] c.c.t.s.interceptor.TransactionAspect    : annotation-TransactionRunning-start---->
2020-05-30 17:30:06.387 DEBUG 4964 --- [nio-8084-exec-8] c.c.t.a.s.impl.AspectBeforeServiceImpl   : around--> groupId-> null,txTransactionLocal->null
2020-05-30 17:30:06.387 DEBUG 4964 --- [nio-8084-exec-8] c.c.t.d.aspect.DataSourceAspect          : getConnection-start---->
2020-05-30 17:30:06.387 DEBUG 4964 --- [nio-8084-exec-8] c.c.tx.datasource.AbstractResourceProxy  : loadConnection -> null !

DemoServiceImpl

package com.example.demo.service.impl;

import com.example.demo.client.Demo2Client;
import com.example.demo.client.Demo3Client;
import com.example.demo.dao.TestDao;
import com.example.demo.entity.Test;
import com.example.demo.service.DemoService;
import com.codingapi.tx.annotation.TxTransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * Created by lorne on 2017/6/26.
 */
@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private TestDao testDao;

    @Autowired
    private Demo2Client demo2Client;

    @Autowired
    private Demo3Client demo3Client;

    @Override
    public List<Test> list() {
        return testDao.list();
    }

    @Override
    **@TxTransaction(isStart = true)**
    @Transactional
    public int save() {

        int rs2 = demo2Client.save();

        int rs3 = demo3Client.save();

        int rs1 = testDao.save();

        int v = 100/0;

        return rs1+rs2+rs3;
    }
}

访问demo1的save接口

//访问save接口:http://localhost:8081/demo/save,触发异常
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sat May 30 17:33:11 CST 2020
There was an unexpected error (type=Internal Server Error, status=500).
Demo3Client#save() failed and fallback failed.

触发回滚JdbcDemo2Application控制台正确日志:

#触发回滚
2020-05-30 17:33:11.654 DEBUG 4468 --- [ntLoopGroup-2-1] c.c.tx.netty.handler.TransactionHandler  : TxManager-response->{"a":"t","c":0,"t":"9Fxhh19M","k":"62kQVGPh"}
2020-05-30 17:33:11.654  INFO 4468 --- [ool-1-thread-19] c.c.t.c.service.impl.ActionTServiceImpl  : accept notify data ->{"a":"t","c":0,"t":"9Fxhh19M","k":"62kQVGPh"}
lcn transaction over, res -> groupId:3OBwlhvN and  state is rollback
2020-05-30 17:33:11.657 DEBUG 4468 --- [      Thread-28] c.c.t.d.relational.LCNDBConnection       : lcnConnection closed groupId:3OBwlhvN
2020-05-30 17:33:11.658  INFO 4468 --- [ool-1-thread-19] c.c.t.c.service.impl.ActionTServiceImpl  : accept notify response res ->1
2020-05-30 17:33:11.658 DEBUG 4468 --- [ool-1-thread-19] .c.t.c.s.i.TransactionControlServiceImpl : send notify data ->{"p":{"d":"1"},"a":"t","k":"62kQVGPh"}
2020-05-30 17:33:11.659 DEBUG 4468 --- [ntLoopGroup-2-1] c.c.tx.netty.handler.TransactionHandler  : TxManager-response->{"d":"","k":"62kQVGPh"}

#clent和manager的心跳数据
2020-05-30 17:33:26.659 DEBUG 4468 --- [ntLoopGroup-2-1] c.c.tx.netty.handler.TransactionHandler  : hart data --->{"p":"{}","a":"h","k":"h"}
2020-05-30 17:33:26.659 DEBUG 4468 --- [ntLoopGroup-2-1] c.c.tx.netty.handler.TransactionHandler  : TxManager-response->{"d":"5","k":"h"}

三、4.0与5.0版本差别

5、0版本从1月份开始大量提交,并已经交由codingApi团队开发维护,两个版本的注解源码是不同的。

/** **4.0版本**
 * Created by lorne on 2017/6/26.
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface TxTransaction {

    /**
     * 是否LCN事务发起方
     * @return true 是:是发起方 false 否:是参与方
     */
    boolean isStart() default false;

    /**
     * 回滚异常
     * @return
     */
    Class<? extends Throwable>[] rollbackFor() default {};

    /**
     * 不回滚异常
     * @return
     */
    Class<? extends Throwable>[] noRollbackFor() default {};

}

/****5.0版本**
 * Created by lorne on 2017/6/26.
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface TxTransaction {

    /**
     * 事务模式 transaction type
     *
     * @return lcn, tcc, txc
     * @see Transactions
     */
    String type() default Transactions.LCN;

    /**
     * 分布式事务传播行为
     *
     * @return 传播行为
     * @see DTXPropagation
     */
    DTXPropagation propagation() default DTXPropagation.REQUIRED;
}

四、参考资料

1、 分布式事务从0到1-认识分布式事务
2、 codingapi/tx-lcn
3、 springcloud LCN分布式事务v4.0 示例demo

文章永久链接:https://tech.souyunku.com/38012

未经允许不得转载:搜云库技术团队 » TX-LCN分布式事务实践

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们