我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何在springcloud中使用bytetcc实现数据的强一致性

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何在springcloud中使用bytetcc实现数据的强一致性

今天就跟大家聊聊有关如何在springcloud中使用bytetcc实现数据的强一致性,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

1 使用背景和约束

公司使用的是springcloud,面临分布式事务的场景的时候,可以使用对springcloud支持比较好的byte-tcc框架,git目前2600星,使用起来也非常方便,原理也很清晰,非常适合学习。 https://github.com/liuyangmin... ,结合cloud有几个重点约束如下,

(1)一个业务接口,需要有三种实现类,分别是try,confirm,cancel,符合tcc的思路。实现方法必须加Transactional,且propagation必须是Required, RequiresNew, Mandatory中的一种。

(2)服务提供方Controller必须添加@Compensable注解,不允许对Feign/Ribbon/RestTemplate等HTTP请求自行进行封装。想想看为什么?

(3)在每个参与tcc事务的数据库中创建bytejta表。

配置上也非常简单,@Import(SpringCloudConfiguration.class),服务提供方,try上面加上

@Service("accountService") @Compensable( interfaceClass = IAccountService.class  , confirmableKey = "accountServiceConfirm" , cancellableKey = "accountServiceCancel" )

confirm和cancel对应的

@Service("accountServiceConfirm")@Service("accountServiceCancel"),

try confirm cancel 对应业务可以理解为 冻结库存/真正扣减库存/恢复库存,或者冻结优惠券/核销优惠券/恢复优惠券这种实际业务场景。

0.5以后datasource自动使用LocalXADataSource,之前需要手动配置

@Bean(name = "dataSource") public DataSource getDataSource() {  LocalXADataSource dataSource = new LocalXADataSource();  dataSource.setDataSource(this.invokeGetDataSource());  return dataSource; }

所以,从配置上看,bytetcc和springcloud结合,一个应该是通过引入自己的SpringCloudConfiguration封装了feign/ribbon/hystrix调用,一个是提供了自己的datasource管理事务。有了自己的datasource,定制自己的transactionManager,就可以在事务前后动手脚,2pc/3pc对事务的管理,体现在控制不同数据库连接上,微服务为主的tcc,对事务的管理体现在控制各个服务的调用上。

2 业务场景思考

bytetcc的tcc,其实try和cancel是配套的,考虑下业务场景:

(1)如果a服务try成功了,b服务try失败,则a服务需要回滚,调用a的cancel。这是普遍流程。

(2)如果a 和 b都try成功了,然后a confirm成功,b的confirm失败,是没有cancel和confirm配对的。b的confirm会不断调用直到成功为止。

因为bytetcc的设计思路是,通过try做好准备工作(如锁定资源),try如果能成功,那么逻辑上confirm一定要成功。如果confirm不成功,则可能是外部环境问题,如网络问题等,那么环境恢复了迟早应该成功confirm。基于这个思想,try和cancel是互逆的,confirm一旦执行就不可逆。

如果要设计confirm也可逆的,那要么cancel里判断是该回滚try还是回滚try+confirm,不清晰且实现很麻烦,或者做成“tccc”加一个对应confirm的cancel,由事务管理器统一判断调用几个cancel,引入太多不确定。所以业务上可以直接这么设计:try成功,那么confirm是一定要成功的。

3 核心组件SpringCloudConfiguration原理分析

第一步就import的SpringCloudConfiguration是重点,通过它的各种自动装配基本可以实现bytetcc的全逻辑。要想扩展spring,那就得扩展各种BeanFactoryPostProcessor,SpringCloudConfiguration本身就是个BeanFactoryPostProcessor,但是postProcessBeanFactory没干啥,应该是引入了各种其他processor进行扩展。如何扩展面临几个问题

(1) 如何识别核心的@Compensable注解?

在byte-tcc的一堆resource文件里,配置了各种bean。既然都Springcloud了为啥还用这种文件bean,可能是兼容cloud之外的场景,增强通用性。在bytetcc-supports-tcc.xml里,有个bean <bean class="org.bytesoft.bytetcc.supports.spring.CompensableAnnotationConfigValidator" />,通过关键代码

clazz.getAnnotation(Compensable.class);扫描得到所有注解了Compensable的类,同时解析出来cancel,confirm对应的类。同时校验一下有没有加transactional注解。后面很多类似的processor都是用这种注解识别需要的bean

(2)如何改造事务管理器,使之适应分布式微服务环境?

在bytetcc-supports-tcc.xml中,定义了改造过的transactionManager,

<bean id="transactionManager" class="org.bytesoft.bytetcc.TransactionManagerImpl" />

这里面重写了begin,commit那一套,结合了tcc专用组件CompensableManager,重新包装了事务操作。

同时,通过SpringCloudConfiguration配置的CompensableHandlerInterceptor,达到transactionInterceptor的效果。

(3)事务如何恢复?

在bytetcc-supports-logger-primary.xml中,有个ResourceAdapterImpl,这个是启动bytetcc后台线程的地方,看bean配置就知道,把两个bean compensableWork和bytetccCleanupWork注入到ResourceAdapterImpl中统一管理,字面意思上看是补偿任务和数据清理任务。这里的compensableWork就是补偿和数据恢复专用的job。

<bean id="compensableResourceAdapter" class="org.bytesoft.transaction.adapter.ResourceAdapterImpl">  <property name="workList">   <list>    <ref bean="compensableWork" />    <ref bean="bytetccCleanupWork" />   </list>  </property> </bean>

追进去看,通过List<Work> workList收集起来work,然后统一用mananger进行start,看work对象本身就继承了Runnable,所以这里是开启了后台线程,进行事务恢复等操作。

(4)具体的请求接口,怎么扩展?

import的SpringCloudConfiguration的代理组件,通过条件注解和配置,根据是否开启hystrix和是否引入HystrixFeign的类,注入针对feign或hystrix的CompensableFeignBeanPostProcessor,如下

@org.springframework.context.annotation.Bean @ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "false", matchIfMissing = true) public CompensableFeignBeanPostProcessor feignPostProcessor() {  return new CompensableFeignBeanPostProcessor(); } @org.springframework.context.annotation.Bean @ConditionalOnProperty(name = "feign.hystrix.enabled") @ConditionalOnClass(feign.hystrix.HystrixFeign.class) public CompensableHystrixBeanPostProcessor hystrixPostProcessor() {  return new CompensableHystrixBeanPostProcessor(); }

以CompensableFeignBeanPostProcessor为例,明显这就是为了对feign接口进行代理的PostProcessor,在postProcessAfterInitialization中,果然通过createProxiedObject(),创建了CompensableFeignHandler的代理类,对springcloud自己的FeignInvocationHandler进行了又一次代理。这样所有@FeignClient的接口都会经过这个handler

同理如果是hystrix的代理,CompensableHystrixBeanPostProcessor会创建CompensableHystrixFeignHandler代理,代替原来的CompensableHystrixInvocationHandler。

通过这两种代理,可以对原生的feign/hystrix组件继续代理,在请求前后做些事情。feign/hystrix其实本身也是结合了ribbon的代理,所以很多spring的扩展就是一层层的代理叠加,为我们扩展组件提供了一种思路。那么bytetcc的核心流程肯定就蕴含在这个请求代理中。

(5)如何控制请求哪一种方法?

bytetcc-supports-springcloud-primary.xml中,有个controller,CompensableCoordinatorController,可以看到里面封装了几种方法,prepare,commit,rollback,额外还有recover,forget,名字上可以看出是恢复,删除事务。结合第四点,原来的feign调用被代理一层,请求的真实url应该被改过,改成了请求这一个controller的方法,通过这个controller再决定后面做什么。

@RequestMapping(value = "/org/bytesoft/bytetcc/prepare/{xid}", method = RequestMethod.POST)@RequestMapping(value = "/org/bytesoft/bytetcc/commit/{xid}/{opc}", method = RequestMethod.POST)@RequestMapping(value = "/org/bytesoft/bytetcc/rollback/{xid}", method = RequestMethod.POST)@RequestMapping(value = "/org/bytesoft/bytetcc/recover/{flag}", method = RequestMethod.GET)@RequestMapping(value = "/org/bytesoft/bytetcc/forget/{xid}", method = RequestMethod.POST)

综上所述,通过新的分布式事务管理器的封装,feign/hystrix请求的代理,controller的控制,后台补偿任务的执行,基本上可以实现强一致性的分布式事务。

4 事务启动-try过程

(1)产生事务

接到用户一个请求时, CompensableHandlerInterceptor会先拦截,这是用户刚发的请求,在这里没找到事务信息什么都不干就返回true了,如果是被调用者,无论是try/confirm/cancel,都会有个事务上下文信息,解析出事务。

CompensableMethodInterceptor->excute(),获得了@transactional和@composable注解,包括 confirm/cancel方法信息,封装到invocation,保存本次调用的一些信息。

transactionInterceptor,调用bytetcc提供的TransactionManagerImpl,提供了新的begin,启动tcc事务。注意这里,如果没有事务上下文,没有compensable注解,那就走一般的begin,就是一般的本地事务。

有了compensable注解,begin就是上面说到的CompensableManager的compensableBegin方法,初始化了事务上下文环境transanctionContext,还生成了个事务id-xid。

(2)方法执行,CompensableFeignInterceptor,把上面生成的事物上下文环境transactionContext,通过序列化生成字符串,放入request的header中,这样发起事务无论调用什么服务,事务的上下文信息就保存在request的header里了。

后面根据feign的代理CompensableFeignHandler发出请求,return this.delegate.invoke(proxy, method, args) delegate就是feign,本质上也是使用原生的feign发请求。

既然本质也是feign调用,思考一下为啥还费事代理一次?事务上下文环境在interceptor里面已经设置到request里了,还代理干啥?

往后看,我认为关键在这里,

beanRegistry.setLoadBalancerInterceptor(new CompensableLoadBalancerInterceptor(this.statefully)

大家知道,feign通过ribbon组件进行的复杂均衡,即chooseInstance,选择请求往哪个实例上发,如果还是轮训或随机,第一次try请求发到某实例,第二次confirm/cancel发到其他实例,别的实例上没有try带来的的事务信息,会非常不方便,也不知道try到底什么情况,

所以这里要多次请求粘滞到一个实例上。所以bytetcc实现了ribbon算法CompensableLoadBalancerRuleImpl,不支持自定义rule

(3)服务接收方接受,首先经过CompensableHandlerInterceptor的preHandle,解析出事务上下文transactionContext,封装成TransactionRequestImpl,并在response里加上一些header信息。这在发起者那里因为没有header的上下文,所以在(1)是什么都不做的

再到 CompensableMethodInterceptor, 解析方法。

再到 TransactionManager,即bytetcc的TransactionManagerImpl,到这里的begin,由于刚接到请求的时候,这时候已经有事务了,所以调用的是一般的本地事务compensableManager.begin(),最后开启一个本地事务,然后执行本地方法,执行commit。

下图可以简单介绍这个过程。

如何在springcloud中使用bytetcc实现数据的强一致性

5 try调用失败,cancel过程

(1)如果有任意一个try失败,那么要把已经成功的try给回滚掉,spring通用的transactionInterceptor的处理过程,invokeWithinTransaction方法,如果有异常,catch住执行

completeTransactionAfterThrowing(),然后到transactionManagerImpl的rollback,继续到CompensableManager的collback

(2)CompensableTransanctionImpl中,fireRemoteParticipantCancel是真正的rollback,里面维护了一个resourcelist,按顺序记录了其他各个服务在try的时候调用的服务,在这里循环这个list调用SpringCloudCoordinator,拼接cancel地址,带着事务id发送请求过去。

(3)接收方,CompensableCoordinatorController的rollback,核心是从CompensableTransactionImpl到SpringContainerContextImpl 的 cancel,得到请求的controller的对应的cancel方法,封装到cancellableKey,然后拿到处理cancel的真实的bean,

Object instance = this.applicationContext.getBean(cancellableKey);this.cancelComplicated(method, instance, args);

进而执行cancel对应的bean的方法。整个过程可以如下概括

如何在springcloud中使用bytetcc实现数据的强一致性

6 try成功,confirm过程

同理,transactionManagerImpl的commit,最终到达CompensableTransactionImp进行fireCommit,先提交本地事务,然后fireRemoteParticipantConfirm,和cancel一模一样,读取resourceList,遍历list发送请求到各个服务端。

各个服务方CompensableCoordinatorController的commit,拿到confirmablekey,找到confirm的bean进行confirm。

如何在springcloud中使用bytetcc实现数据的强一致性

7 “compensable”的补偿

(1)如果cancel,commit有失败(失败包含runtimeexception和自定义的一些异常),那么如何进行补偿,上面提到的一开始就启动的CompensableWork线程的run里面,其实有个while(true),每隔100秒循环一次,调用组件TransactionRecovery(看名字就知道恢复事务用的)的timingRecover,就是定时回复,会调用到CompensableTransactionImpl的recoveryRollback/recoveryCommit,还是SpringCloudCoordinator发送的请求。

(2)如果出现宕机,重启后也是通过CompensableWork线程的run,第一步是init,尝试恢复现有的事务。

a 如果try没有执行完就down机,恢复时把已执行的try给cancel掉。因为事务一般是业务请求触发的,down机就请求失败了,没必要重启后还恢复刚才的请求。
b 如果是confirm/cancel有没成功的,会一直定时进行confirm/cancel。

看完上述内容,你们对如何在springcloud中使用bytetcc实现数据的强一致性有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网行业资讯频道,感谢大家的支持。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何在springcloud中使用bytetcc实现数据的强一致性

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何在springcloud中使用bytetcc实现数据的强一致性

今天就跟大家聊聊有关如何在springcloud中使用bytetcc实现数据的强一致性,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1 使用背景和约束公司使用的是springclou
2023-06-06

如何使用Redis实现分布式数据一致性

如何使用Redis实现分布式数据一致性引言:随着互联网的快速发展,分布式系统已成为许多企业的首选架构。在分布式系统中,数据的一致性是非常关键的。Redis作为一种高性能、可扩展的键值存储系统,被广泛应用于分布式系统中,下面将介绍如何使用Re
如何使用Redis实现分布式数据一致性
2023-11-07

实战解析:如何利用数据库触发器实现数据一致性

数据库触发器是一种数据库对象,当数据库中的某个表发生INSERT、UPDATE或DELETE操作时,触发器会自动执行。利用数据库触发器可以实现数据一致性,确保数据在不同表之间保持一致。
实战解析:如何利用数据库触发器实现数据一致性
2024-02-08

PHP中如何使用事务管理确保数据一致性?

php 中的事务管理通过确保数据库操作的原子性来维护数据一致性。它允许在事务内执行操作,并在成功完成后提交更改,或在失败时回滚更改。通过按顺序执行以下步骤,可以利用事务管理:开始事务、执行操作、提交事务或回滚事务。这确保了要么所有操作成功,
PHP中如何使用事务管理确保数据一致性?
2024-05-06

Golang 函数在分布式系统中实现数据一致性的方法

问题:如何在 go 中使用函数实现分布式系统数据一致性?答案:使用函数类型声明和使用函数:func(param_type_1 param_name_1, ..., param_type_n param_name_n) return_type
Golang 函数在分布式系统中实现数据一致性的方法
2024-04-19

MySQL到DB2: 如何实现数据的完整转移和一致性?

MySQL和DB2是两种广泛使用的关系型数据库管理系统(RDBMS),在某些情况下,我们可能需要将数据从MySQL迁移到DB2。本文将介绍如何实现数据的完整转移和一致性,确保迁移过程顺利进行。数据迁移计划的制定在开始数据迁移之前,我们需要制
2023-10-22

怎么在SpringCloud中使用Eureka实现服务之间的传递数据

这期内容当中小编将会给大家带来有关怎么在SpringCloud中使用Eureka实现服务之间的传递数据,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、使用RestTemplate+Ip方式:1、传递数据
2023-06-15

如何使用MySQL的外键和约束提高数据完整性和一致性?

如何使用MySQL的外键和约束提高数据完整性和一致性?在MySQL数据库中,外键和约束是两个重要的概念,它们可以帮助提高数据的完整性和一致性。在本文中,我们将详细讨论如何使用MySQL的外键和约束来实现这个目标,并提供一些代码示例。一、外键
2023-10-22

如何在Cassandra中实现数据的事务性操作

Cassandra是一个分布式数据库系统,不支持传统关系型数据库中的ACID事务。Cassandra的设计目标是为了提供高可用性和横向扩展性,因此牺牲了部分事务性的功能。尽管Cassandra不支持完整的ACID事务,但可以通过以下方式来
如何在Cassandra中实现数据的事务性操作
2024-04-09

在springboot中使用mybatis如何实现多数据源

这篇文章给大家介绍在springboot中使用mybatis如何实现多数据源,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务。我们
2023-05-31

如何在Python中进行数据可靠性存储和迁移,以及数据一致性的保证和校验

如何在Python中进行数据可靠性存储和迁移,以及数据一致性的保证和校验引言:数据的安全性和一致性对于任何应用程序都至关重要。在Python中,我们可以使用一些技术和库来确保数据的可靠性存储和迁移,以及数据一致性的保证和校验。本文将介绍几种
2023-10-22

Python中如何实现一个线程安全的并发缓存对象,保证读写一致性和数据安全性

Python中如何实现一个线程安全的并发缓存对象,保证读写一致性和数据安全性在多线程的环境下,对共享数据进行读写操作需要考虑到线程安全的问题。当多个线程同时对一个缓存对象进行读写操作时,可能会导致数据不一致或者数据丢失的问题。为了解决这个问
2023-10-22

如何在MySQL中实现数据库复制和高可用性

要在MySQL中实现数据库复制和高可用性,可以采用以下方法:MySQL复制:MySQL提供了内置的复制功能,可以使用主从复制或主主复制来实现数据库复制。主从复制是将数据从一个MySQL服务器(主服务器)复制到另一个MySQL服务器(从服务器
如何在MySQL中实现数据库复制和高可用性
2024-03-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录