spring cloud 集成TX-LCN5.0.2使用LCN模式实现分布式事务

各位童鞋注意啦,5.0.2的版本在多节点下是存在问题的,解决方法:https://blog.csdn.net/zhuwei_clark/article/details/103711929

项目源码地址:https://github.com/daxian-zhu/online_edu

对于微服务化的趋势,分布式事务是一个绕不去的坎,现在有很多开源的软件 tx-lcn,byte-tcc, seata(阿里系),jta

今天我介绍的是tx-lcn一个国内的开源软件。官网地址:http://www.txlcn.org/zh-cn/

spring boot 1.5X以下的的建议使用4.X的版本。

原因我就不多说了,官网上面又,我直接说代码了。

第一步TM的部署:

新建一个module,并修改POM文件如下:

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
	<dependency>
	    <groupId>com.codingapi.txlcn</groupId>
	    <artifactId>txlcn-tm</artifactId>
	    <version>5.0.2.RELEASE</version>
	</dependency>
  </dependencies>

 然后新建启动类,新增加注解:EnableTransactionManagerServer

@SpringBootApplication
@EnableTransactionManagerServer
public class LCNApplication {
	
	public static void main(String[] args) throws Exception {
		SpringApplication.run(LCNApplication.class, args);
	}

}

 新建application.properties(这里我本来打算用yml的,但是不知道为啥yml配置启动报错)

spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.95.250.218:3306/tx_manager?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.username=lcn
spring.datasource.password=edu.,Dev123
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
#redis配置
spring.redis.host=47.95.250.218
spring.redis.password=zhudaxian;.,68NB
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=60000
#事物执行时间
tx-lcn.manager.dtx-time=600000

注意最后的事物执行时间设置,默认是8000,源码的是这样计算超时时间的

    public boolean isDTXTimeout() {
        if (!hasTxContext()) {
            throw new IllegalStateException("non TxContext.");
        }
        return (System.currentTimeMillis() - txContext().getCreateTime()) >= clientConfig.getDtxTime();
    }

反正我发现这个算法很扯淡,我点了下请求最多几秒,但是这个方法算出来居然30多秒,我就很蛋疼了。所以我先把他设成很大的值,还有如果在client端设置tx-lcn.client.dtx-time这个属性,启动就报错,真不知道这源码咋回事。所以这里在TM这里设置的,TM的设置是会覆盖TC的设置的。

启动TM,访问http://localhost:7970  密码:codingapi 就能进入后台管理界面

到这里TM配置就完事了。

第二步:TC配置,所有的TC配置基本上是一样的,我就介绍一个了

1)修改POM文件 

    <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>

2)修改application.yml配置

#lcn配置
tx-lcn: 
  client: 
    manager-address: 127.0.0.1:8070
  springcloud:
    loadbalance:
      enabled: true

微服务集群且用到 LCN事务模式时,为保证性能请开启TX-LCN重写的负载策略。

tx-lcn.springcloud.loadbalance.enabled=true

也就是上面我配置的属性。这是官网上面说的。

3)修改启动类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableResJWTTokenStore //OAuth2 使用 JWT 解析令牌
//开启分布式事务
@EnableDistributedTransaction
public class StudentApplication {
	
	public static void main(String[] args) throws Exception {
		SpringApplication.run(StudentApplication.class, args);
	}

}

增加注解:EnableDistributedTransaction

4)修改业务类

	@LcnTransaction(propagation = DTXPropagation.REQUIRED) //分布式事务注解
	@Transactional //本地事务注解
	@Override
	public Result updateStudentInfo(StudentInfo studentInfo) {
		if(studentInfo.getId()==null) {
			return Result.failure(ResponeCode.FAIL_1001);
		}
		UC_User uc_User = getUser();
		studentInfo.setUpdateUser(uc_User.getId());
		studentInfo.setUpdateDate(DateUtil.getNowDateTime());
		//更新学生信息
		studentInfoDao.updateStudentInfo(studentInfo);
		String groupId = TracingContext.tracing().groupId();
		String applicationId = Transactions.getApplicationId();
		System.out.println(groupId);
		System.out.println(applicationId);
		//构造客户信息,并更新客户信息
		CustomerInfo customer = new CustomerInfo();
		customer.setId(studentInfo.getCustomerId());
		customer.setAge(studentInfo.getAge());
		customer.setSex(studentInfo.getSex());
		customer.setName(studentInfo.getName());
		customer.setContactMonile(studentInfo.getContactMonile());
		customerService.updateCustomerInfo(customer.getId(), customer);
		//更新用户信息,暂时不处理
		
		return Result.OK();
	}

增加注解@LcnTransaction(propagation = DTXPropagation.REQUIRED) //分布式事务注解 @Transactional //本地事务注解

关于DTXPropagation源码里面有解释

    /**
     * 当前没有分布式事务,就创建。当前有分布式事务,就加入
     */
    REQUIRED,

    /**
     * 当前没有分布式事务,非分布式事务运行。当前有分布式事务,就加入
     */
    SUPPORTS;

所以一般事物发起方使用REQUIRED,事物参与方使用SUPPORTS

5)熔断配置

	@Override
	public Result updateCustomerInfo(String id, CustomerInfo customerInfo) {
		DTXUserControls.rollbackGroup(TracingContext.tracing().groupId());
		logger.info("调用{}失败","getCustomerInfo");
	    return Result.failure(ResponeCode.FAIL_1002);
	}

注意增加代码:DTXUserControls.rollbackGroup(TracingContext.tracing().groupId());  我在这里被坑了好久。

6)配置事物参与方

	//事物的参与方
	@LcnTransaction(propagation = DTXPropagation.SUPPORTS)
	@Transactional
	public Result updateCustomerInfo(CustomerInfo customerInfo) {
		if(customerInfo.getId()==null) {
			return Result.failure(ResponeCode.FAIL_1001);
		}
		String groupId = TracingContext.tracing().groupId();
		String applicationId = Transactions.getApplicationId();
		System.out.println(groupId);
		System.out.println(applicationId);
		groupId = null;
		if(groupId.equals("aa")) {
			System.out.println("aa");
		}
		UC_User uc_User = getUser();
		customerInfo.setUpdateUser(uc_User.getId());
		customerInfo.setUpdateDate(DateUtil.getNowDateTime());
		customerInfoDao.updateCustomerInfo(customerInfo);
		return Result.OK();
	}

这里我设置了一个null值,故意抛出一个空指针。

7)开启各个服务进行测试,我这里student服务是事物发起方(A方),customer服务是事物参与方(B方)

现在是A调用B,B服务会出错,我们看下A和B的事物是不是会回滚

student原始数据状态:

customer原始数据状态:

 调用update方法:主要是修改姓名

 执行完成我们先看B服务,如下图我们发现B服务抛出异常

39ff9b84d54537
customer:9005
2019-07-31 16:27:51.610 ERROR 15420 --- [nio-9005-exec-3] c.c.txlcn.tc.core.DTXServiceExecutor     : business code error @group(39ff9b84d54537)
Wed Jul 31 16:27:51 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2019-07-31 16:27:51.621 ERROR 15420 --- [nio-9005-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
	at com.clark.online_edu.customer.service.impl.CustomerInfoServiceImpl.updateCustomerInfo(CustomerInfoServiceImpl.java:45) ~[classes/:na]
	at com.clark.online_edu.customer.service.impl.CustomerInfoServiceImpl$$FastClassBySpringCGLIB$$2be9a8ca.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at com.codingapi.txlcn.tc.core.DTXLocalControl.doBusinessCode(DTXLocalControl.java:44) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at com.codingapi.txlcn.tc.core.DTXServiceExecutor.transactionRunning(DTXServiceExecutor.java:91) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at com.codingapi.txlcn.tc.aspect.weave.DTXLogicWeaver.runTransaction(DTXLogicWeaver.java:96) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at com.codingapi.txlcn.tc.aspect.TransactionAspect.runWithLcnTransaction(TransactionAspect.java:93) ~[txlcn-tc-5.0.2.RELEASE.jar:5.0.2.RELEASE]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_101]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_101]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_101]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_101]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) ~[spring-aop-5.0.9

我们在看A服务:

39ff9b84d54537
student:9006
2019-07-31 16:27:49.518  INFO 5280 --- [nio-9006-exec-5] c.c.o.student.config.FeignConfiguration  : 保持请求头
2019-07-31 16:27:51.630  INFO 5280 --- [nio-9006-exec-5] c.c.o.s.s.impl.CustomerServiceImpl       : 调用getCustomerInfo失败

进入熔断业务执行正常。接下来我们看下数据库的变化:

student

customer:

 我们发现数据都没有变化,数据被回滚了。A没有出现异常数据也回滚了。

8)我们现在注释掉空指针,再次测试,修改customer的代码

	//事物的参与方
	@LcnTransaction(propagation = DTXPropagation.SUPPORTS)
	@Transactional
	public Result updateCustomerInfo(CustomerInfo customerInfo) {
		if(customerInfo.getId()==null) {
			return Result.failure(ResponeCode.FAIL_1001);
		}
		String groupId = TracingContext.tracing().groupId();
		String applicationId = Transactions.getApplicationId();
		System.out.println(groupId);
		System.out.println(applicationId);
//		groupId = null;
//		if(groupId.equals("aa")) {
//			System.out.println("aa");
//		}
		UC_User uc_User = getUser();
		customerInfo.setUpdateUser(uc_User.getId());
		customerInfo.setUpdateDate(DateUtil.getNowDateTime());
		customerInfoDao.updateCustomerInfo(customerInfo);
		return Result.OK();
	}

发起请求测试:

student

customer:

 事物提交成功。

发布了149 篇原创文章 · 获赞 36 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/zhuwei_clark/article/details/97927248