2阶段提交分布式事务中间件Raincat
yuyutoo 2024-10-16 15:47 10 浏览 0 评论
架构设计
流程图
流程图原理详解
首先用户发起request请求进入consume端(也可以是controller层),controller层进入AOP切面,开启分布式事务
- 会与txManager通信,创建事务组
- 发起业务方法调用point.proceed(),这一步是分布式事务调用RPC方法的入口,会一个一个调用RPC方法
- 主线程调用provder1 提供的RPC方法,同样会进入切面,此时,会启动另一个线程去调用该RPC方法,当前线程wait
调用失败,调用业务方法线程,会唤醒等待的主线程,并把异常信息返回,走的就是one fial rollback这条线,消费方获取异常信息,自动回滚当前事务,分布式事务结束。 调用成功 业务线程,将唤醒主线程,并返回数据。业务线程同txManager 通信 将自己加入到当前事务组中,业务线程进入等待,等待txManager指令,commit or rollback 即 图中的4,5,6
- 主线程继续调用provider2提供的RPC方法 ,同样会进入AOP切面,当前主线程等待,启动一个业务线程去调用业务方法
调用失败,调用业务方法线程,会唤醒等待的主线程,并把异常信息返回,走的就是two fial rollback这条线,消费方获取异常信息,自动回滚当前事务,并与txManager进行通信(two TransactionGroup fail 这条线),txManager 发起rollback指令(two fail rollback 这条线),通知provider1 rollback 调用成功 业务线程,将唤醒主线程,并返回数据。业务线程同txManager 通信 将自己加入到当前事务组中,业务线程进入等待,等待txManager指令,commit or rollback 即 图中的9,10,11
- 主线程point.proceed()方法成功执行,会向txManager发出commit请求,txManager接收请求后,会检查各模块的网络通信等状态,符合条件后,发出commit指令,即 14。通知各个模块提交自己的事务。
三阶段提交最大的问题,在于当txManager发出commit指令后,业务方down机,或者网络通信断了。怎么解决事务一致性的问题。我们这么想,当txManager 发出commit指令的时候,其实业务方法都没有任何异常,都是执行成功的,那么数据就是需要提交的。那么我们本地的补偿机制正是为了该场景而考虑,如果down机,或者网络异常。本地服务恢复后,会执行补偿,达到数据的一致性。如果业务方是集群部署的话,会立刻执行补偿,达到数据的一致性。
重要提醒,如果调用服务方是timeout,那么会判断当前事务不成功,这次分布式事务统一不会执行。
快速开始:
Spring-Boot-Starter
导出
- 首先引入rpc框架所支持的jar包,注意:版本号,根据你拉代码自己打包上传到私服一致
- Dubbo
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-spring-boot-starter-dubbo</artifactId> <version>${your version}</version> </dependency>
- SpringCloud
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-spring-boot-starter-springcloud</artifactId> <version>${your version}</version> </dependency>
- Motan
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-spring-boot-starter-motan</artifactId> <version>${your version}</version> </dependency>
- 在你的 application.yml 中新增如下配置:
raincat: tx : txManagerUrl: http://localhost:8761 serializer: kroy nettySerializer: kroy compensation: true compensationCacheType : db txDbConfig : driverClassName : com.mysql.jdbc.Driver url : jdbc:mysql://10.4.4.202:3306/tx?useUnicode=true&characterEncoding=utf8 username : root password : 123123 # repositorySupport : redis # txRedisConfig : # hostName : 192.168.1.68 # port : 6379 # password : # repositorySupport : zookeeper # tx-zookeeper-config : # host : 92.168.1.73:2181 # sessionTimeOut : 100000 # rootPath : /tx # repositorySupport : mongodb # tx-mongo-config : # mongoDbUrl : localost:27017 # mongoDbName : xiaoyu # mongoUserName : xiaoyu # mongoUserPwd : 123456 # repositorySupport : file # tx-file-config: # path : /consume # prefix : consume
TxManager配置详解
- application.properties 主要是配置tmManager的http服务端口,redis信息,netty相关信息 注意:因为现在tmManager 是自己向自己注册,所以http端口(server.port)应该要与eureka的端口一致。
server.port=8761 txManager 的http端口
tx.manager.netty.port=9998 对业务方提供的TCP 端口。
tx.manager.netty.serialize=kryo netty 序列化方式,注意应该要与业务方的序列化方式一致。
- bootstrap.yml 主要是配置eureka的相关属性,比如renew时间,注册地址等
- 部署集群配置: 1.修改application.properties中的 server.port 如: 第一份服务为 server.port=8761 tx.manager.netty.port=9998; 第二份服务为:server.port=8762 tx.manager.netty.port=9999; 2. 修改bootstrap.yml中的eureka:client:serviceUrl:defaultZone:http://localhost:8761/eureka/,http://localhost:8762/eureka/ 再依次启动
业务方配置详解
@TxTransaction 该注解为分布式事务的切面(AOP point),如果业务方的service服务需要参与分布式事务,则需要加上此注解
applicationContext.xml 详解:
<!-- Aspect 切面配置,是否开启AOP切面--> <aop:aspectj-autoproxy expose-proxy="true"/> <!--扫描分布式事务的包--> <context:component-scan base-package="com.raincat.*"/> <!--启动类属性配置--> <bean id="txTransactionBootstrap" class="com.raincat.core.bootstrap.TxTransactionBootstrap"> <property name="txManagerUrl" value="http://192.168.1.66:8761"/> <property name="serializer" value="kryo"/> <property name="nettySerializer" value="kryo"/> <property name="blockingQueueType" value="Linked"/> <property name="compensation" value="true"/> <property name="compensationCacheType" value="db"/> <property name="txDbConfig"> <bean class="com.raincat.common.config.TxDbConfig"> <property name="url" value="jdbc:mysql://192.168.1.78:3306/order?useUnicode=true&characterEncoding=utf8"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="password" value="password"/> <property name="username" value="xiaoyu"/> </bean> </property> </bean> TxTransactionBootstrap 详解(具体参见com.raincat.core.config.TxConfig): <!--这里配置的TxManager http请求的IP:PORT (如果TxManager有改动,这里要跟着改动)--> <property name="txManagerUrl" value="http://192.168.1.66:8761"/> <!-- 与txManager通信的序列化方式,spi扩展支持 kroy,hessian protostuff 推荐使用kroy--> <property name="nettySerializer" value="kryo"/> <!-- 线程池中的队列类型 spi扩展支持 Linked Array SynchronousQueue--> <property name="blockingQueueType" value="Linked"/> <!--线程池中的拒绝策略 spi扩展支持 Abort Blocking CallerRuns Discarded Rejected--> <property name="rejectPolicy" value="Abort"/> <!--开启本地补偿(默认开启)--> <property name="compensation" value="true"/> <!--本地数据序列化方式 spi扩展支持 java kroy,hessian protostuff 推荐使用kroy--> <property name="serializer" value="kryo"/>
本地数据保存配置与详解(spi扩展支持db,redis,zookeeper,mongodb,file),详情配置请参照sample工程:
- 本地数据存储为数据库(数据库支持mysql,oracle ,sqlServer),当业务模块为集群时,推荐使用 会自动创建表,表名称为 tx_transaction_模块名称(applicationName),每个模块配置成一样的补偿方式,如果是用db进行存储,请使用同一个库来存储。
<!--配置补偿类型为db--> <property name="compensationCacheType" value="db"/> <property name="txDbConfig"> <bean class="com.raincat.common.config.TxDbConfig"> <!--数据库url--> <property name="url" value="jdbc:mysql://192.168.1.78:3306/order?useUnicode=true&characterEncoding=utf8"/> <!--数据库驱动名称 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="password" value="1234567"/> <property name="username" value="xiaoyu"/> </bean> </property>
- 本地数据存储为redis,当业务模块为集群时,推荐使用。(更多配置请参考 com.happylifeplat.transaction.core.config.TxRedisConfig)
<!--配置补偿类型为reids--> <property name="compensationCacheType" value="redis"/> <property name="txRedisConfig"> <bean class="com.raincat.common.config.TxRedisConfig"> <!--redis host--> <property name="hostName" value="192.168.1.78"/> <!--redis port--> <property name="port" value="6379"/> <!--redis 密码 (有密码就配置,无密码则不需要配置)--> <property name="password" value=""/> </bean> </property>
- 本地数据存储为zookeeper,当业务模块为集群时,推荐使用
<!--配置补偿类型为zookeeper--> <property name="compensationCacheType" value="zookeeper"/> <property name="txZookeeperConfig"> <bean class="com.raincat.common.config.TxZookeeperConfig"> <!--zookeeper host:port--> <property name="host" value="192.168.1.66:2181"/> <!--zookeeper session过期时间--> <property name="sessionTimeOut" value="2000"/> <!--zookeeper 根节点路径--> <property name="rootPath" value="/tx"/> </bean> </property>
- 本地数据存储为mongodb,当业务模块为单节点时,可以使用。会自动创建集合,集合名称为 tx_transaction_模块名称(applicationName) 这里mongdb连接方式采用3.4.0版本推荐使用的Sha1,不是CR模式,同时mongdb应该开启权限认证,使用者需要注意
<!--配置补偿类型为mongodb--> <property name="compensationCacheType" value="mongodb"/> <property name="txMongoConfig"> <bean class="com.raincat.common.config.TxMongoConfig"> <!--mongodb url--> <property name="mongoDbUrl" value="192.168.1.78:27017"/> <!--mongodb 数据库--> <property name="mongoDbName" value="happylife"/> <!--mongodb 用户名--> <property name="mongoUserName" value="xiaoyu"/> <!--mongodb 密码--> <property name="mongoUserPwd" value="123456"/> </bean> </property>
- 本地数据存储为file,当业务模块为单节点时,可以使用。创建的文件名称TX_ + prefix配置 + 模块名称
<!--配置补偿类型为file--> <property name="compensationCacheType" value="file"/> <property name="txFileConfig"> <bean class="com.raincat.common.config.TxFileConfig"> <!--指定文件路径(可填可不填,不填时候,默认就是当前项目所在的路径)--> <property name="path" value=""/> <!--指定文件前缀,生成文件名称--> <property name="prefix" value="consume"/> </bean> </property>
dubbo用户指南
导出
dubbo用户指南(jar包的版本问题可以自己git源代码,然后编译上传到自己公司的私服)
- 配置并且启动txManager,在aoolication.properties修改你的服务端口,redis配置,具体可以参考配置详解
- 在你的dubbo服务端添加jar包,并在需要参与分布式事务的方法上添加 @TxTransaction注解
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-dubbo</artifactId> <version>${your.version}</version> </dependency>
- 配置applicationContext.xml,如下所示,如有其他问题可以参考配置详解
<!-- Aspect 切面配置,是否开启AOP切面--> <aop:aspectj-autoproxy expose-proxy="true"/> <!--扫描分布式事务的包--> <context:component-scan base-package="com.raincat.*"/> <!--启动类属性配置--> <bean id="txTransactionBootstrap" class="com.raincat.core.bootstrap.TxTransactionBootstrap"> <!--这里的url是txMangager的ip+端口--> <property name="txManagerUrl" value="http://192.168.1.66:8761"/> <property name="serializer" value="kryo"/> <property name="nettySerializer" value="kryo"/> <property name="blockingQueueType" value="Linked"/> <property name="compensation" value="true"/> <property name="compensationCacheType" value="db"/> <property name="txDbConfig"> <bean class="com.raincat.common.config.TxDbConfig"> <property name="url" value="jdbc:mysql://192.168.1.78:3306/order?useUnicode=true&characterEncoding=utf8"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="password" value="password"/> <property name="username" value="xiaoyu"/> </bean> </property> </bean>
- 在dubbo的消费方,也是如上配置,配置好以后,启动项目.
快速体检,运行dubbo-sample( 使用者JDK必须为1.8)
步骤一:
配置txManaager, 修改application.properties中你自己的redis配置
启动TxManagerApplication
注意:如果需要修改服务端口,则应该保证与eureka:client:serviceUrl:defaultZone中的端口一致
步骤二:
- 引入依赖包(sample已经引入)
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-dubbo</artifactId> <version>${you.version}</version> </dependency>
- 执行 raincat-dubbo-sample 工程 sql文件 dubbo-sample.sql
- 在每个工程下的application.yml 中配置您的数据库连接(只需要改ip和端口)
- 在工程下的spring-dubbo.xml 中配置您的zookeeper注册中心
- 在每个工程下 applicationContext.xml中的TxDbConfig 配置您的补偿数据库连接,提供单独的库来存储。
- 在需要做分布式事务的接口上加上注解 @TxTransaction (sample已经加上)
- 依次启动order模块,stock 模块 ,consume模块
- 访问http://localhost:8888/swagger-ui.html 进行测试体验
springcloud用户指南
导出
springcloud用户指南(jar包的版本问题可以自己git源代码,然后编译上传到自己公司的私服)
- 配置并且启动txManager,在aoolication.properties修改你的服务端口,redis配置,具体可以参考配置详解
- 在你的springcloud服务端添加jar包,并在需要参与分布式事务的方法上添加 @TxTransaction注解
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-springcloud</artifactId> <version>${your.version}</version> </dependency>
- 配置applicationContext.xml,如下所示,如有其他问题可以参考配置详解
<!-- Aspect 切面配置,是否开启AOP切面--> <aop:aspectj-autoproxy expose-proxy="true"/> <!--扫描分布式事务的包--> <context:component-scan base-package="com.raincat.*"/> <!--启动类属性配置--> <bean id="txTransactionBootstrap" class="com.raincat.core.bootstrap.TxTransactionBootstrap"> <!--这里的url是txMangager的ip+端口--> <property name="txManagerUrl" value="http://192.168.1.66:8761"/> <property name="serializer" value="kryo"/> <property name="nettySerializer" value="kryo"/> <property name="blockingQueueType" value="Linked"/> <property name="compensation" value="true"/> <property name="compensationCacheType" value="db"/> <property name="txDbConfig"> <bean class="com.raincat.common.config.TxDbConfig"> <property name="url" value="jdbc:mysql://192.168.1.78:3306/order?useUnicode=true&characterEncoding=utf8"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="password" value="password"/> <property name="username" value="xiaoyu"/> </bean> </property> </bean>
- 在springcloud的消费方,也是如上配置,配置好以后,启动项目.
快速体检,运行springcloud-sample( 使用者JDK必须为1.8)
步骤一:
配置txManaager, 修改application.properties中你自己的redis配置 启动TxManagerApplication
注意:如果需要修改服务端口,则应该保证与eureka:client:serviceUrl:defaultZone中的端口一致
步骤二:
- 引入依赖包(sample已经引入)
<dependency> <groupId>com.raincat</groupId> <artifactId>raincat-springcloud</artifactId> <version>${your.version}</version> </dependency>
- 执行 raincat-springcloud-sample 工程 sql文件 springcloud-sample.sql
- 在每个工程下 application.yml 中配置您的数据库连接(只需要改ip和端口)
- 在每个工程下 applicationContext.xml中的TxDbConfig 配置您的补偿数据库连接,提供单独的数据库来存储。
- 在需要做分布式事务的接口上加上注解 @TxTransaction (sample已经加上)
- 依次启动AliPayApplication,WechatApplication ,PayApplication
- 访问http://localhost:8881/pay-service/swagger-ui.html 进行体验测试
启动事务管理后台(admin)
导出
raincat-admin 启动教程
启动前提:分布式事务项目已经部署并且运行起来,用户根据自己的RPC框架进行使用 dubbo 用户 springcloud 用户
- 首先用户使用的JDK必须是1.8+ 本地安装了git ,maven ,执行以下命令
git clone https://github.com/yu199195/Raincat.git maven clean install
- 使用你的开发工具打开项目,比如idea Eclipse
步骤一: 配置并且启动transaction-admin
- 在项目中的application.properties文件中,修改您的服务端口,redis配置等配置:
#admin项目的tomcat端口 server.port=8888 #admin项目的上下文 server.context-path=/admin server.address=0.0.0.0 #admin项目的应用名称 spring.application.name=tx-transaction-admin # admin项目激活的类型,支持db,file,mongo,zookeeper,redis 下文会继续讲解 spring.profiles.active=db # txManager redis 配置 #集群配置 #tx.redis.cluster=true #tx.redis.cluster.nodes=127.0.0.1:70001;127.0.1:7002 #tx.redis.cluster.redirects=20 #单机配置 tx.redis.cluster=false tx.redis.hostName=192.168.1.68 #redis主机端口 tx.redis.port=6379 #tx.redis.password=happylifeplat01 # admin管理后台的用户名,用户可以自己更改 tx.admin.userName=admin # admin管理后台的密码,用户可以自己更改 tx.admin.password=admin #采用二阶段提交项目的应用名称集合,这个必须要填写 recover.application.list=alipay-service,wechat-service,pay-service #事务补偿最大重试次数 recover.retry.max=10 #事务补偿的序列方式 recover.serializer.support=kryo #dbSuport 采用db进行的补偿方式的配置 recover.db.driver=com.mysql.jdbc.Driver recover.db.url=jdbc:mysql://192.168.1.68:3306/tx?useUnicode=true&characterEncoding=utf8 recover.db.username=xiaoyu recover.db.password=Wgj@555888 #redis 采用redis进行补偿方式的配置 recover.redis.cluster=false recover.redis.hostName=192.168.1.68 recover.redis.port=6379 recover.redis.password= #recover.redis.clusterUrl=127.0.0.1:70001;127.0.1:7002 #mongo 采用mongo 进行补偿方式的配置 recover.mongo.url=192.168.1.68:27017 recover.mongo.dbName=happylife recover.mongo.userName=xiaoyu recover.mongo.password=123456 #zookeeper 采用zookeeper进行补偿的方式配置 recover.zookeeper.host=192.168.1.132:2181 recover.zookeeper.sessionTimeOut=200000
配置解释
- 关于txManager的redis配置:其实就是在txManager项目中的redis配置,在这里需要配置成一样的; 注意,如果你的redis是集群模式请参考集群配置,并注释掉单机redis配置,同理redis单机模式也一样。
- 关于 recover.application.list配置:这里需要配置每个参与二阶段分布式事务的系统模块的applicationName,多个模块用 "," 分隔,这里必须要配置。
- 关于 recover.serializer.support 配置,这里是指参与二阶段分布式事务系统中,配置事务补偿信息的序列化方式。
- 关于 spring.profiles.active 配置 admin项目激活的类型,支持db,file,mongo,zookeeper, 这里是指参与二阶段分布式事务系统中,配置事务补偿信息存储方式,如果您用db存储,那这里就配置成db,同时配置好 recover.db等信息。 其他方式同理。 注意,每个模块请使用相同的序列化方式和存储类型
- 关于 tx.admin 等配置。 这里就是管理后台登录的用户与密码,用户可以进行自定义更改。
步骤二:修改 index.html
<!--href 修改成你的ip 端口--> <a id="serverIpAddress" style="display: none" href="http://192.168.1.132:8888/admin">
步骤三: 运行 AdminApplication 中的main方法。
步骤四:在浏览器访问 http://ip:prot/admin ,输入用户名,密码登录。
相关推荐
- 深度解读Spring框架的核心原理
-
深度解读Spring框架的核心原理在Java开发的世界里,提到Spring框架,就像提起一位久经沙场的老将,它几乎成了企业级应用开发的代名词。那么,这个被无数开发者膜拜的框架究竟有何独特之处?今天,我...
- 「Spring认证」Spring 框架概述
-
Spring是最流行的企业Java应用程序开发框架。全球数以百万计的开发人员使用SpringFramework来创建高性能、易于测试和可重用的代码。Spring框架是一个开源的Java...
- 学习Spring框架 这一篇就够了
-
1.spring概述1.1Spring是什么(理解)...
- Spring框架双核解析:IOC与AOP的本质与实战
-
#Spring核心#IOC容器#AOP编程#Java框架设计...
- Spring Boot与传统Spring框架的对比:探索Java开发的新境界
-
SpringBoot与传统Spring框架的对比:探索Java开发的新境界在Java生态系统中,Spring框架无疑是一个里程碑式的存在。从最初的简单依赖注入容器,到如今覆盖企业级开发方方面面的庞大...
- Spring MVC框架源码深度剖析:从入门到精通
-
SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...
- Spring框架入门
-
一.spring是什么?Spring是分层...
- 程序员必知必会技能之Spring框架基础——面向切面编程!
-
面向切面编程AOP(AspectOrientedProgramming)与OOP(ObjectOrientedProgramming,面向对象编程)相辅相成。AOP提供了与OOP不同的抽象软件结...
- Spring Security安全框架深度解读:为你的应用穿上“钢铁铠甲”
-
SpringSecurity安全框架深度解读:为你的应用穿上“钢铁铠甲”在现代网络世界里,保护我们的应用程序免受各种威胁攻击至关重要。而在这个过程中,SpringSecurity框架无疑是我们最可...
- Spring框架的设计哲学与实现:打造轻量级的企业级Java应用
-
Spring框架的设计哲学与实现:打造轻量级的企业级Java应用Spring框架自2003年诞生以来,已成为企业级Java应用开发的代名词。它不仅仅是一个框架,更是一种设计理念和哲学的体现。本文将带你...
- Spring框架深度解析:从核心原理到底层实现的全方位避坑指南
-
一、Spring框架核心概念解析1.控制反转(IoC)与依赖注入(DI)Spring的核心思想是通过IoC容器管理对象的生命周期和依赖关系。传统开发中,对象通过new主动创建依赖对象,导致高耦合;而S...
- Java框架 —— Spring简介
-
简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入...
- Spring 框架概述,模块划分
-
Spring框架以控制反转(InversionofControl,IoC)和面向切面编程(Aspect-OrientedProgramming,AOP)为核心,旨在简化企业级应用开发,使开发者...
- spring框架怎么实现依赖注入?
-
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入。具体介绍如下:●属性set...
- Spring框架详解
-
Spring是一种开放源码框架,旨在解决企业应用程序开发的复杂性。一个主要优点就是它的分层体系结构,层次结构让你可以选择要用的组件,同时也为J2EE应用程序开发提供了集成框架。 Spring特征...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)