最近在搭建TX-LCN分布式事务时遇到不少坑,为了这些坑浪费了很长时间,为了别的小伙伴不在和我似的在这些坑上遇到相同的麻烦特把我的搭建过程整理出来,给大家参考

官方说明:http://www.txlcn.org/zh-cn/docs/demo/env.html

前期准备工作:

Mysql数据库、Redis缓存、Eureka注册服务。

一、首先我们要先搭建TX-LCN的服务端。即官网上说的tx-manager模块。

1、新建SpringBoot项目

截止到发文前springboot官网版本是2.1.5,因此创建完成后,需要修改版本信息,降低版本号。TX-LCN项目中已经集成了redis和数据库连接,因此这里不需要加入数据库和redis相关的依赖。只加入eureka 客户端的依赖即可。

2、修改pom.xml文件,修改版本号信息及,增加相关的依赖信息,如图:

生成的pom.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.manager</groupId>
	<artifactId>tc-manager</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>tc-manager</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Finchley.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.codingapi.txlcn</groupId>
			<artifactId>txlcn-tm</artifactId>
			<version>5.0.2.RELEASE</version>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3、修改application.properties文件,增加tm相关的配置信息,如redis和数据库等。

注意:tx-lcn.manager.port=8070 这个配置,它用于配置LCN的监听端口,我们在下面配置客户端服务里需要用到这个端口号。


server.port=7970

spring.application.name=TransactionManager

eureka.client.service-url.defaultZone=http://admin:admin@localhost:8761/eureka
eureka.instance.appname=transaction-manager
eureka.instance.prefer-ip-address=true



#JDBC 数据库配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://10.115.132.12:3306/tx?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456

#数据库方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

# 第一次运行可以设置为: create, 为TM创建持久化数据库表
#spring.jpa.hibernate.ddl-auto=validate
spring.jpa.hibernate.ddl-auto=update

# 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=codingapi

# 分布式事务锁超时时间 默认为-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/***@**.com



# 开启日志,默认为false
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}

#  redis 的设置信息. 线上请用Redis Cluster
spring.redis.host=10.115.132.12
spring.redis.port=6379
spring.redis.password=123456

4、修改启动文件,增加TX-LCN的注解@EnableTransactionManagerServer。代码如下:

package com.manager;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import com.codingapi.txlcn.tm.config.EnableTransactionManagerServer;

@SpringBootApplication
@EnableDiscoveryClient
@EnableTransactionManagerServer
public class TcManagerApplication {

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

}

5、创建完成后就可以启动服务了。控制台出现如下信息表示TX-LCN的服务端搭建完成。

2019-06-09 18:18:30.544  INFO 14812 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2b662a77: startup date [Sun Jun 09 18:18:30 CST 2019]; root of context hierarchy
2019-06-09 18:18:30.716  INFO 14812 --- [           main] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2019-06-09 18:18:30.737  INFO 14812 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$d2cfd437] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.6.RELEASE)

2019-06-09 18:18:30.878  INFO 14812 --- [           main] com.manager.TcManagerApplication         : No active profile set, falling back to default profiles: default
2019-06-09 18:18:30.878  INFO 14812 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@67b9b51a: startup date [Sun Jun 09 18:18:30 CST 2019]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@2b662a77
2019-06-09 18:18:31.347  INFO 14812 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2019-06-09 18:18:31.662  INFO 14812 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2019-06-09 18:18:31.678  INFO 14812 --- [           main] .RepositoryConfigurationExtensionSupport : Spring Data Redis - Could not safely identify store assignment for repository candidate interface com.codingapi.txlcn.tm.support.db.jpa.TxExceptionRepository.
2019-06-09 18:18:31.806  INFO 14812 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'dataSource' with a different definition: replacing [Root bean: class [null]; scope=refresh; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=false; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]] with [Root bean: class [org.springframework.aop.scope.ScopedProxyFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in BeanDefinition defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]]
2019-06-09 18:18:31.958  INFO 14812 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=0df06d89-18ce-38e7-9c32-fa4e5efaeac5
2019-06-09 18:18:31.977  INFO 14812 --- [           main] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2019-06-09 18:18:32.070  INFO 14812 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$b6b5d13a] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-06-09 18:18:32.105  INFO 14812 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$d2cfd437] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-06-09 18:18:32.491  INFO 14812 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 7970 (http)
2019-06-09 18:18:32.516  INFO 14812 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-06-09 18:18:32.516  INFO 14812 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.34
2019-06-09 18:18:32.524  INFO 14812 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [C:\Program Files\Java\jre1.8.0_201\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:/Program Files/Java/jre1.8.0_201/bin/server;C:/Program Files/Java/jre1.8.0_201/bin;C:/Program Files/Java/jre1.8.0_201/lib/amd64;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\iCLS\;C:\Program Files\Intel\Intel(R) Management Engine Components\iCLS\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\TortoiseSVN\bin;C:\Users\lenovo\AppData\Local\Microsoft\WindowsApps;;E:\sts-3.9.0.RELEASE;;.]
2019-06-09 18:18:32.684  INFO 14812 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-06-09 18:18:32.684  INFO 14812 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1806 ms
2019-06-09 18:18:32.856  WARN 14812 --- [ost-startStop-1] c.n.c.sources.URLConfigurationSource     : No URLs will be polled as dynamic configuration sources.
2019-06-09 18:18:32.856  INFO 14812 --- [ost-startStop-1] c.n.c.sources.URLConfigurationSource     : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2019-06-09 18:18:32.863  INFO 14812 --- [ost-startStop-1] c.netflix.config.DynamicPropertyFactory  : DynamicPropertyFactory is initialized with configuration sources: com.netflix.config.ConcurrentCompositeConfiguration@521808b8
2019-06-09 18:18:34.071  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2019-06-09 18:18:34.072  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'webMvcMetricsFilter' to: [/*]
2019-06-09 18:18:34.072  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2019-06-09 18:18:34.072  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2019-06-09 18:18:34.072  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2019-06-09 18:18:34.072  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpTraceFilter' to: [/*]
2019-06-09 18:18:34.072  INFO 14812 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2019-06-09 18:18:34.157  INFO 14812 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-06-09 18:18:34.424  INFO 14812 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-06-09 18:18:34.471  INFO 14812 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2019-06-09 18:18:34.471  INFO 14812 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
	name: default
	...]
2019-06-09 18:18:34.549  INFO 14812 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.2.17.Final}
2019-06-09 18:18:34.549  INFO 14812 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2019-06-09 18:18:34.596  INFO 14812 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2019-06-09 18:18:34.706  INFO 14812 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect
2019-06-09 18:18:35.368  INFO 14812 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2019-06-09 18:18:36.133  WARN 14812 --- [           main] c.n.c.sources.URLConfigurationSource     : No URLs will be polled as dynamic configuration sources.
2019-06-09 18:18:36.133  INFO 14812 --- [           main] c.n.c.sources.URLConfigurationSource     : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2019-06-09 18:18:36.196  INFO 14812 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-06-09 18:18:36.289  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@67b9b51a: startup date [Sun Jun 09 18:18:30 CST 2019]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@2b662a77
2019-06-09 18:18:36.336  WARN 14812 --- [           main] aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2019-06-09 18:18:36.367  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/provider/email-to/{email}],methods=[POST]}" onto public boolean com.codingapi.txlcn.tm.support.txex.provider.DefaultExUrlProvider.email(java.lang.String,com.codingapi.txlcn.tm.support.db.domain.TxException)
2019-06-09 18:18:36.367  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.codingapi.txlcn.tm.support.restapi.RedirectController.index()
2019-06-09 18:18:36.383  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/login],methods=[POST]}" onto public com.codingapi.txlcn.tm.support.restapi.vo.Token com.codingapi.txlcn.tm.support.restapi.AdminController.login(java.lang.String) throws com.codingapi.txlcn.common.exception.TxManagerException
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/logs/{page} || /admin/logs/{page}/{limit} || /admin/logs],methods=[GET]}" onto public com.codingapi.txlcn.tm.support.restapi.vo.TxLogList com.codingapi.txlcn.tm.support.restapi.AdminController.txLogList(java.lang.Integer,java.lang.Integer,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.Integer) throws com.codingapi.txlcn.common.exception.TxManagerException
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/tx-manager],methods=[GET]}" onto public com.codingapi.txlcn.tm.support.restapi.vo.TxManagerInfo com.codingapi.txlcn.tm.support.restapi.AdminController.getTxManagerInfo()
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/app-mods/{page} || /admin/app-mods/{page}/{limit} || /admin/app-mods],methods=[GET]}" onto public com.codingapi.txlcn.tm.support.restapi.vo.ListAppMods com.codingapi.txlcn.tm.support.restapi.AdminController.listAppMods(java.lang.Integer,java.lang.Integer)
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/exceptions/{page} || /admin/exceptions || /admin/exceptions/{page}/{limit}],methods=[GET]}" onto public com.codingapi.txlcn.tm.support.restapi.vo.ExceptionList com.codingapi.txlcn.tm.support.restapi.AdminController.exceptionList(java.lang.Integer,java.lang.Integer,java.lang.Integer,java.lang.Integer)
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/exceptions],methods=[POST]}" onto public boolean com.codingapi.txlcn.tm.support.restapi.AdminController.deleteExceptions(com.codingapi.txlcn.tm.support.restapi.vo.DeleteExceptions) throws com.codingapi.txlcn.common.exception.TxManagerException
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/logs],methods=[DELETE]}" onto public boolean com.codingapi.txlcn.tm.support.restapi.AdminController.deleteLogs(com.codingapi.txlcn.tm.support.restapi.vo.DeleteLogsReq) throws com.codingapi.txlcn.common.exception.TxManagerException
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/tm-cluster],methods=[GET]}" onto public java.util.List<com.codingapi.txlcn.tm.cluster.TMProperties> com.codingapi.txlcn.tm.support.restapi.AdminController.tmList() throws com.codingapi.txlcn.common.exception.FastStorageException
2019-06-09 18:18:36.387  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/log/transaction-info],methods=[GET]}" onto public com.alibaba.fastjson.JSONObject com.codingapi.txlcn.tm.support.restapi.AdminController.transactionInfo(java.lang.String,java.lang.String) throws com.codingapi.txlcn.common.exception.TxManagerException
2019-06-09 18:18:36.388  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin/transaction-info],methods=[DELETE]}" onto public boolean com.codingapi.txlcn.tm.support.restapi.AdminController.deleteTransactionInfo(java.lang.String,java.lang.String,java.lang.String) throws com.codingapi.txlcn.common.exception.TxManagerException
2019-06-09 18:18:36.388  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/manager/refresh],methods=[POST]}" onto public boolean com.codingapi.txlcn.tm.support.restapi.TxManagerController.refresh(com.codingapi.txlcn.txmsg.params.NotifyConnectParams) throws com.codingapi.txlcn.txmsg.exception.RpcException
2019-06-09 18:18:36.388  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-06-09 18:18:36.388  INFO 14812 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-06-09 18:18:36.404  INFO 14812 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-06-09 18:18:36.404  INFO 14812 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-06-09 18:18:36.960  INFO 14812 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 2 endpoint(s) beneath base path '/actuator'
2019-06-09 18:18:36.970  INFO 14812 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-06-09 18:18:36.970  INFO 14812 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2019-06-09 18:18:36.971  INFO 14812 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto protected java.util.Map<java.lang.String, java.util.Map<java.lang.String, org.springframework.boot.actuate.endpoint.web.Link>> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-06-09 18:18:37.008  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2019-06-09 18:18:37.009  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'logDbProperties' has been autodetected for JMX exposure
2019-06-09 18:18:37.010  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'dataSource' has been autodetected for JMX exposure
2019-06-09 18:18:37.018  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'environmentManager' has been autodetected for JMX exposure
2019-06-09 18:18:37.019  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'refreshScope' has been autodetected for JMX exposure
2019-06-09 18:18:37.020  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'configurationPropertiesRebinder' has been autodetected for JMX exposure
2019-06-09 18:18:37.023  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'environmentManager': registering with JMX server as MBean [org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager]
2019-06-09 18:18:37.029  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'refreshScope': registering with JMX server as MBean [org.springframework.cloud.context.scope.refresh:name=refreshScope,type=RefreshScope]
2019-06-09 18:18:37.037  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'logDbProperties': registering with JMX server as MBean [com.codingapi.txlcn.logger.db:name=logDbProperties,type=LogDbProperties]
2019-06-09 18:18:37.039  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'configurationPropertiesRebinder': registering with JMX server as MBean [org.springframework.cloud.context.properties:name=configurationPropertiesRebinder,context=67b9b51a,type=ConfigurationPropertiesRebinder]
2019-06-09 18:18:37.043  INFO 14812 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
2019-06-09 18:18:37.048  INFO 14812 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2019-06-09 18:18:37.059  INFO 14812 --- [           main] o.s.c.n.eureka.InstanceInfoFactory       : Setting initial instance status as: STARTING
2019-06-09 18:18:37.095  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Initializing Eureka in region us-east-1
2019-06-09 18:18:37.139  INFO 14812 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using JSON encoding codec LegacyJacksonJson
2019-06-09 18:18:37.140  INFO 14812 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using JSON decoding codec LegacyJacksonJson
2019-06-09 18:18:37.241  INFO 14812 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using XML encoding codec XStreamXml
2019-06-09 18:18:37.241  INFO 14812 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using XML decoding codec XStreamXml
2019-06-09 18:18:37.423  INFO 14812 --- [           main] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
2019-06-09 18:18:37.464  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Disable delta property : false
2019-06-09 18:18:37.465  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Single vip registry refresh property : null
2019-06-09 18:18:37.465  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Force full registry fetch : false
2019-06-09 18:18:37.465  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Application is null : false
2019-06-09 18:18:37.465  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Registered Applications size is zero : true
2019-06-09 18:18:37.465  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Application version is -1: true
2019-06-09 18:18:37.465  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2019-06-09 18:18:37.606  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : The response status is 200
2019-06-09 18:18:37.621  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Starting heartbeat executor: renew interval is: 30
2019-06-09 18:18:37.621  INFO 14812 --- [           main] c.n.discovery.InstanceInfoReplicator     : InstanceInfoReplicator onDemand update allowed rate per min is 4
2019-06-09 18:18:37.621  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Discovery Client initialized at timestamp 1560075517621 with initial instances count: 2
2019-06-09 18:18:37.621  INFO 14812 --- [           main] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application TRANSACTION-MANAGER with eureka with status UP
2019-06-09 18:18:37.621  INFO 14812 --- [           main] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1560075517621, current=UP, previous=STARTING]
2019-06-09 18:18:37.621  INFO 14812 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_TRANSACTION-MANAGER/DESKTOP-242V863:TransactionManager:7970: registering service...
2019-06-09 18:18:37.668  INFO 14812 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 7970 (http) with context path ''
2019-06-09 18:18:37.668  INFO 14812 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 7970
2019-06-09 18:18:37.668  INFO 14812 --- [           main] com.manager.TcManagerApplication         : Started TcManagerApplication in 7.483 seconds (JVM running for 7.935)
2019-06-09 18:18:37.668  INFO 14812 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_TRANSACTION-MANAGER/DESKTOP-242V863:TransactionManager:7970 - registration status: 204
2019-06-09 18:18:37.778  INFO 14812 --- [           main] c.c.t.t.n.i.NettyRpcServerInitializer    : Socket started on 127.0.0.1:8070 
2019-06-09 18:18:37.793  INFO 14812 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0xd80096dd] REGISTERED
2019-06-09 18:18:37.793  INFO 14812 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0xd80096dd] BIND: /127.0.0.1:8070
2019-06-09 18:18:37.793  INFO 14812 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0xd80096dd, L:/127.0.0.1:8070] ACTIVE
2019-06-09 18:18:37.871  INFO 14812 --- [           main] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2019-06-09 18:18:37.871  INFO 14812 --- [           main] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
2019-06-09 18:18:38.043  INFO 14812 --- [           main] c.c.t.t.s.s.impl.ManagerServiceImpl      : Acquired machine id 40, max machine id is: 2251799813685246

二、TX-LCN的服务端搭建完成后,下一步我们就可以针对我们自己的项目设置分布式事务了。配置过程如下:

1、第一步我们先创建我们自己的微服务。如果有现成的微服务也可以在原有的微服务的基础上改造。为了测试方便,我这里创建了两个最简单的微服务。分别叫tc-client-1和tc-client-2。由于只是测试分布式事务,因此这两个项目我建设的完全一样。只有名称不一样。

2、项目创建后,修改依赖信息形成的pom.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.client</groupId>
	<artifactId>tc-client-1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>tc-client-1</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Finchley.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.4</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.9</version>
		</dependency>	
		
			<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>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3、修改application.properties文件,我这里将application.properties文件改名为application.yml文件,因此格式是yml格式。

注意:

tx-lcn:
  client:
    manager-address: 127.0.0.1:8070

这个配置信息是服务器端的监听端口,在服务器端通过tx-lcn.manager.port,tx-lcn.manager.host这两个属性进行的配置

tc-client-1项目的配置代码如下:


server:
  port: 10002
  
spring: 
  application:
    name: client1

  
eureka:
  instance:
    appname: client1
    prefer-ip-address: true
  client: 
    service-url:
      defaultZone: http://admin:admin@localhost:8761/eureka

---
spring: 
  datasource: 
    password: 123456
    username: root
    url: jdbc:mysql://10.115.132.12:3306/tx?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid: 
      validation-query: select '*'
      initial-size: 1
      max-active: 20
      max-wait: 1000
      filters: stat
      pool-prepared-statements: false
  redis: 
    port: 6379
    host: 10.115.132.12
    database: 0
    jedis:
      pool:
        max-idle: 20
        max-active: 20
        max-wait: -1
        min-idle: 0
    timeout: 1000

  transaction:
    rollback-on-commit-failure: true

mybatis: 
  mapper-locations: classpath*:com/client/dao/xml/*.xml
  type-aliases-package: com.client.dao.mapper

tx-lcn:
  client:
    manager-address: 127.0.0.1:8070 

feign:
  client:
    config:
      default: 
        connect-timeout: 10000
        read-timeout: 20000
      service-test:
        connect-timeout: 10000
        read-timeout: 20000
  hystrix:
    enabled: true

management:
  endpoints: 
    web:
      exposure:
        include:
          "*"
  endpoint:
    health:
      show-details: always

 tc-client-2项目的配置代码为:


server:
  port: 10003
  
spring: 
  application:
    name: client2

  
eureka:
  instance:
    appname: client2
    prefer-ip-address: true
  client: 
    service-url:
      defaultZone: http://admin:admin@localhost:8761/eureka

---
spring: 
  datasource: 
    password: 123456
    username: root
    url: jdbc:mysql://10.115.132.12:3306/tx?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid: 
      validation-query: select '*'
      initial-size: 1
      max-active: 20
      max-wait: 1000
      filters: stat
      pool-prepared-statements: false
  redis: 
    port: 6379
    host: 10.115.132.12
    database: 0
    jedis:
      pool:
        max-idle: 20
        max-active: 20
        max-wait: -1
        min-idle: 0
    timeout: 1000

  transaction:
    rollback-on-commit-failure: true

mybatis: 
  mapper-locations: classpath*:com/client/dao/xml/*.xml
  type-aliases-package: com.client.dao.mapper

tx-lcn:
  client:
    manager-address: 127.0.0.1:8070 

feign:
  client:
    config:
      default: 
        connect-timeout: 10000
        read-timeout: 20000
      service-test:
        connect-timeout: 10000
        read-timeout: 20000
  hystrix:
    enabled: true

management:
  endpoints: 
    web:
      exposure:
        include:
          "*"
  endpoint:
    health:
      show-details: always

4、在tx这个数据库中创建我们要操作的表test_1和test_2。这两个表的类型必须为innodb。MYISAM格式是不支持事务的。结构如下图:

5、开始编整合事务。

tc-client-1 项目的结构如下:

其中TcClient1Application作为启动类,需要加入@EnableDistributedTransaction和@EnableTransactionManagement这两个注解。生成的代码如下:

package com.client;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;

@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableDiscoveryClient
@ComponentScan("com.client.service")
@MapperScan("com.client.dao")
@EnableDistributedTransaction
@EnableTransactionManagement
public class TcClient1Application {

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

}

TestService作用事务的处理类也需要加入事务注解,    @LcnTransaction(propagation = DTXPropagation.SUPPORTS)
    @Transactional 。由于这个是被作用被调用者使用的,因此需要在注解中加入propagation = DTXPropagation.SUPPORTS配置。形成的代码如下:


package com.client.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.client.dao.mapper.TestMapper;
import com.client.model.TestModel;
import com.codingapi.txlcn.tc.annotation.DTXPropagation;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;


@Service
public class TestService {

	@Autowired
	private TestMapper mapper;
	
	@LcnTransaction(propagation = DTXPropagation.SUPPORTS)
	@Transactional
	public int insert() throws Exception{
		TestModel test = new TestModel();
		test.setId("1");
		test.setName("name");
		test.setType(1);
		return mapper.insert(test);
		
		
	}
}

其他model类,dao层的数据库操作方法大概按原来的方式写即可。没有特别需要注意的。

6、下一步就是修改tc-client-2项目了,这个项目是需要调用tc-client-1这个微服务的。它的结构如下:

从两个结构图中可以看出,这个项目多了一个ClientService类用于调用tc-client-1这个项目。它的代码如下:


package com.client.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(name="client1")
public interface ClientService {

	@RequestMapping("/test")
	public int insertTest() throws Exception;
}

这个项目的启动类也是TcClient1Application,它的代码和tc-client-1项目没有任何区别,都是在原有项目的基础上增加了@EnableDistributedTransaction
@EnableTransactionManagement

这两个注解。

package com.client;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.codingapi.txlcn.tc.config.EnableDistributedTransaction;

@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableDiscoveryClient
@ComponentScan("com.client.service")
@MapperScan("com.client.dao")
@EnableDistributedTransaction
@EnableTransactionManagement
public class TcClient1Application {

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

}

这个项目中的TestService服务调用类有一点不同,代码如下:


package com.client.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.client.dao.mapper.TestMapper;
import com.client.model.TestModel;
import com.codingapi.txlcn.tc.annotation.DTXPropagation;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;


@Service
public class TestService {

	@Autowired
	private TestMapper mapper;
	
	@Autowired
	private ClientService client;
	
	@LcnTransaction
	@Transactional
	public int insert() throws Exception{
		client.insertTest();
		TestModel test = new TestModel();
		test.setId("1");
		test.setName("name");
		test.setType(1);
		return mapper.insert(test);
		
		
	}
}

它虽然也加了@LcnTransaction这个注解,但由于这里是调用其他服务的,因此这个注解中不能配置其他属性。

这个项目中dao层和Model层相关的代码如tc-client-1完全相同,小伙伴们按照自己喜欢的方式写就可以了。

到这里就大功告成了。小伙伴们可以自己修改一个数据库结构让其中一个数据插入不成功,看看成果。

注意:

1、数据库表结构必须为innoDB

2、在启动类上一定要加入@EnableDistributedTransaction

3、当调用其他服务时@LcnTransaction注解不能加入其他参数

4、被调用的服务@LcnTransaction注解一定要加入propagation = DTXPropagation.SUPPORTS这个配置

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐