经验整理-1-分布式事务-100-@

--------------------------分布式事务--------------------------

你们公司是如何处理分布式事务的?

特别严格的场景,用的是 TCC 来保证强一致性;然后其他的一些场景基于阿里的 RocketMQ 来实现分布式事务。

网关调账务记账用的 TCC 强一致性方案;如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案

 

CAP理论?

一致性可用分区:想要数据强一致性安全,就会慢,可用性就差。要么CP,要么AP。


?分布式事务解决方案有几种?

①传统模式使用Jta+Atomikos
②PC与3PC实现的区别
③支付回调通知补偿型
④使用阿里巴巴TCC补偿框架
⑤使用可靠消息模式
⑥使用LCN框架解决分布式事务
⑦阿里GTS框架解决分布式事务

重点解释:

  • TCC 方案---Try、Confirm、Cancel
  • 可靠消息Mq最终一致性方案---rocketMq
  • 本地消息表---
  • XA 方案(Jta+Atomikos)---适合单系统里,跨多个库的分布式事务,XA不合理(规定和规范,是要求每个服务只能操作自己对应的一个数据库
  • 使用LCN框架解决分布式事务
  • 最大努力通知方案(柔性事务BASE补偿几次)---适合被动方处理结果 不影响主动方的处理结果,如银行通知、商户通知,采用类似支付宝回调机制,但是,要考虑补偿机制,重试机制,还要考虑幂等性,重复性问题。

 

-------------------TCC 方案-------------

?工作原理或实现原理?

TCC 的全称是:Try、Confirm、Cancel。
Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留。
Confirm 阶段:这个阶段说的是在各个服务中执行实际的操作。
Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)

?的作用或优点或应用场景?

很少人使用,事务回滚实际上是严重依赖于你自己写代码来回滚和补偿。
一般来说相关的,跟钱打交道的,支付交易相关的场景,我们会用 TCC,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,保证在资金上不会出现问题

?我搭建过,如何搭建?

?搭建和使用过程中遇到哪些问题?

?*与*的区别?

-------------------rabbitMQ可靠消息队列最终一致性方案-------------

?rabbitMQ可靠消息队列最终一致性方案,工作原理或实现原理?

(A 系统先发送一个 prepared 消息到 mq,如果这个 prepared 消息发送失败那么就直接取消操作别执行了;
如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉 mq 发送确认消息,如果失败就告诉 mq 回滚消息;
如果发送了确认消息,那么此时 B 系统会接收到确认消息,然后执行本地的事务;
mq 会自动定时轮询所有 prepared 消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息却发送失败了。
这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。
这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用 RocketMQ 支持的,要不你就自己基于类似 ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的。)

利用MQ自带的事务消息,少数支持如RocketMQ,主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
原理:(和上面三、本地消息表事务的改进版,相比优点在于发送者业务提交失败队列不会再下发到消费者,缺点在于没考虑消费者是否消费到问题
RocketMQ(借鉴了两阶段提交理论--这个我考察过)有一个消息准备阶段和提交阶段。
准备阶段,生产者发送准备消息到队列。(消费者)MQ服务端收到准备消息后再反馈给生产者接收成功,此时也代表发送成功
提交阶段,生产者提交本地业务块的事物,再发送提交消息到队列(中断,没发过去会走检查机制)(消费者)MQ服务端收到提交消息后,真正开始下发到消费者,执行业务块。
检查机制,生产者要实现一个 check 接口,根据检查结果,定义返回策略决定是 rollback 还是继续 commit
这样就保证了消息发送与本地事务同时成功或同时失败。(并不保证消费端业务也一定执行成功)

?的作用或优点或应用场景?

?我搭建过,如何搭建?

?搭建和使用过程中遇到哪些问题?

?rabbitMQ版本?

发送者计数3次发送MQ失败进入死信队列,通知人工处理死信队列,减少数据库压力),
示例:放款功能,P2P业务系统(下单)和网关系统(拆分存管和账务下单)。(当然是举例,网关很多不同的业务,开标、放款、债转、还款等,重构代价比较大,业务迭代时间快,没有时间重构,目前把压力给我的账务了,账务是压力上来了,不得不重构,如果没压力,公司不会支持去做重构,中小型公司普遍的现象)
现在要实现一个保证两个系统分布式系统一致性的思路:
示例1-跨系统间,
官方版
RabbitMQ解决分布式事务原理: 采用最终一致性原理。需要保证以下三要素
1、确认生产者一定要将数据投递到MQ服务器中(采用MQ消息确认机制)
2、MQ消费者消息能够正确消费消息,采用手动ACK模式(注意重试幂等性问题)
3、如何保证第一个事务先执行,采用补偿机制,在创建一个补单消费者进行监听,如果订单没有创建成功,进行补单。


自已改进版
RabbitMQ解决分布式事务原理: 采用最终一致性原理。需要保证以下三要素
1、确认生产者投递,一定要将数据投递到MQ服务器中(采用MQ发送消息确认机制
2、确认MQ消费者消费,消息能够正确消费,采用手动ACK模式(注意重试幂等性问题,不要依赖队列重试,走业务代码重试)
3、保证第一个系统事务先执行:首先,队列放在后,且catch队列发送异常,不回滚。然后重写生产者发送消息确认机制,响应失败补偿,3次告警。(减轻DB压力)
真实项目举例:
1. (先本地一致性)业务系统操作下单并巧用确认机制保证提交到队列。当业务下单的时候,在本地同一个事务中(依靠数据库本地事务保证一致性),业务下单操作后,把下单消息发往队列中,直接提交事物(不用消息表,能舍弃强事物,减少DB操作),当然这里也会对发送MQ阶段做近似一致性:重写业务生产者发送消息确认机制,并把CorrelationData 类重写了,增加消息体字段和计数字段,用来重发和失败计数,如果正常,保留原来机制,什么都不做,如果ack失败,计数+1,等2秒并重发,如果3次了,报警,人工干预可能是有问题,在这里也可以先扔死信队列,我们是人工重新补偿。(再看一下建消息表+kafka的弊端,它会在发送成功之后有个删DB消息表的操作,还要定时任务去把消息表未发送成功状态的消息读出来重发,效率慢)
2. (下游系统被消费)网关系统保证操作成功下单才ack队列。网关系统监听队列,采用手动ACK模式,(一定不要抛异常,不要依赖队列重试,拿不到重试次数,除非想办法设置),业务记账块封装成可重试执行块,重试超过3次未知异常失败(余额不足不算,比如超时),告警,并Nack该消息,进入死信队列,待人工处理。

-------------------本地消息表-------------

?工作原理或实现原理?

A 系统在自己本地一个事务里操作同时,插入一条数据到消息表;
接着 A 系统将这个消息发送到 MQ 中去;
B 系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理消息;
B 系统执行成功之后,就会更新自己本地消息表的状态以及 A 系统消息表的状态;
如果 B 系统处理失败了,那么就不会更新消息表状态,那么此时 A 系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到 MQ 中去,让 B 再次处理;
这个方案保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那边成功为止。

?的作用或优点或应用场景?

严重依赖于数据库的消息表来管理事务,高并发场景不适合。

?我搭建过,如何搭建?

?搭建和使用过程中遇到哪些问题?

?*与*的区别?

-------------------XA 方案(Jta+Atomikos)-------------

?工作原理或实现原理?

两段提交:jta+atomikos实现了两阶段提交分布式事务。一个协调控制多个参与者一起提交完成。适合於单体系统多数据库的场景。我们微服务都是一个系统一个库,这个不适合。---可以考虑LCN,自搭一个分布式协调者系统来管理多个参考者系统。

?的作用或优点或应用场景?

某个系统内部如果出现跨多个库(但不合规,现在都要求每个服务只能操作自己对应的一个数据库,通过调用别的服务的接口访问别人的数据库

?我搭建过,如何搭建?

?搭建和使用过程中遇到哪些问题?

?*与*的区别?

 

-------------------LCN-------------
?工作原理或实现原理?

全局事物(协调者-LCN服务端管理本地事物(参与者-LCN客户端

1.LCN客户端(发起方和参与方都必须要注册到事务协调者中) 建立一个长连接(和dubbo类似,netty长连接)。
2.发起方调用参与方之前,会向TxManager事务协调者创建一个事务的分组id。
3.发起方调用参与方时,分组id放在请求头中,传给参与方。
4.参与方拿到请求头中分组id,执行完业务后,返回结果响应,自已也会采用假关闭,等待真正提交该事务。
5.发起方收到响应后, 执行事务提交,告知协调者。参与方随后跟着提交。

订单服务(发起方)调用库存服务接口(参与方)之后,如果订单服务(发起方)执行没有问题的下,
订单服务(发起方)使用对应的事务分组id,通知给TxManager事务协调者,让后TxManager事务协调者在根据该事务分组id,通知给所有的参与方提交事务。

?配合集群原理?

有两个地方建立长连接:基目启动时,通过nginx与新的LCN服务器节点建立长连接;宕机时,再通过nginx的故障转移与新的LCN服务器节点建立长连接

 

?的作用或优点或应用场景?

?我搭建过,如何搭建?

1.首先通过nginx配置多个tm-lcn协调者负载均衡配置,让后 LCN客户端启动项目的时候访问nginx负载均衡地址获取lcn协议通讯IP地址和端口号,并且对该连接保持长连接。 
2.因为LCN客户端与TM协调者保持的是长连接,当tm协调者宕机之后,LCN客户端会立即重新进入到获取负载均衡地址lcn协议通讯IP地址和端口号。
 

?搭建和使用过程中遇到哪些问题?

问题1:debug调试的时候,发现,发起者事物成功了,参与方事物回滚了,分布式事物不一致,
解决:后来看源码是有一个超时机制,参与方在5秒没有收到协调者通知情况下,会默认执行回滚。调试时可设置增长超时时间

-------------------最大努力通知方案(柔性事务BASE补偿几次)-------------

?工作原理或实现原理?

采用类似支付宝回调机制,但是,要考虑补偿机制,重试机制,还要考虑幂等性,重复性问题。

  1. 系统 A 本地事务执行完之后,发送个消息到 MQ;
  2. 这里会有个专门消费 MQ 的最大努力通知服务,这个服务会消费 MQ 然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统 B 的接口;
  3. 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。

?的作用或优点或应用场景?

?我搭建过,如何搭建?

?搭建和使用过程中遇到哪些问题?

?*与*的区别?

------------------2PC与3PC-------------

?二阶段提交协议实现为什么会有阻塞问题?

2PC:
第一阶段 -表决阶段(表决阶段准备过程)    
第二阶段 - 提交阶段(执行阶段,执行提交过程)
准备过程(请求阶段)没问题提交过程有公共资源同步阻塞问题。执行提交过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态
二阶段提交缺点单点故障 由于协调者的重要性,一旦协调者发生故障参与者会一直阻塞下去,尤其在提交阶段,参与者锁定公共资源未释放

3PC:(改动点---1、协调者和参与者间引入超时机制来解决单点故障问题、超时不再等待减少阻塞;2、插入一个准备阶段)
来了解一下三阶段提交:(把原来的表决阶段分为询问和准备阶段,询问只问不要求执行
询问阶段canCommit(预先检查阶段,带有超时机制):询问其他参与者,如果有一个返回NO或超时,就回滚事物。(协调者只需要回答能否,而不需要做真正的操作
准备阶段(PreCommit准备过程,锁资源) ,
提交阶段(执行阶段,执行提交过程)
对比,三阶段提交最大优点就是降低了参与者执行阶段的阻塞范围,并且能够在出现单点故障后继续达成一致。
缺点就是在去除阻塞的情况下引入了新的问题,那就是准备阶段,参与者1接收到消息后执行成功,其他参与者有失败的,协调者就会向所有参与者发出回滚命令,但此时参与者1(网络出现问题)接收不到协调者命令,超时之后参与者1一个人提交,和其他参与者不一致问,

2PC和3PC的对比?

2pc的问题:

问题1:阻塞问题,提交阶段,协调者单点故障挂了,所有参与者会一直锁着公共资源不释放等待协调者命令,就会出现阻塞问题----解决方法:3PC在协调者和参与者间引入超时机制,超时默认提交,当然又有新的不一致问题1
问题1.2:单点故障问题,如上,一个单点故障影响了其他协调者无法再进行工作----
解决方法:如上
问题2:不一致问题,提交阶段,协调者和参与者1都挂了,且参与者1是执行了回滚但未通知协调者就挂了。当协调者和参与者1恢复时,假设协调者从其他没挂掉的参与者处得到了提交成功的反馈,提交了事物。这就和参与者1执行了回滚形成了不一致性。----解决方法:3PC插入一个询问阶段,如果参与者1提交阶段会回滚,那其询问阶段也是回滚应答,其他参与者也会回滚,所以是一致性的

3PC的问题:

问题1:超时机制带来的新的不一致问题,询问阶段全OK,然后进入准备阶段,参与者1接收到消息后执行成功,反馈给协调者时超时且中断了,协调者2PC就有的特点会把这种超时当成失败发回滚命令,其他参与者都收到了回滚命令,但此时参与者1(网络出现问题)接收不到协调者命令,超时之后参与者1会根据3pc新增的参与者至协调者超时机制默认一个人提交,就造成和其他参与者不一致问题(这个特性导致的:2pc本身已支持协调者等待参与者超时会回滚

3PC为什么比2PC好?

就是上面解决的两个问题:
1、超时机制(注意是:新增参与者等待协调者超时,2pc本身已支持协调者等待参与者超时会回滚)--解决阻塞问题、单点故障问题
2、插入一个询问阶段---解决协调者和参与者1都挂了的不一致性问题(这个特性导致的:2pc本身已支持协调者等待参与者超时会回滚



 

 

--------MQ削峰填谷-----

示例2-账务系统内部削峰的同时,如何保证分布式安全

?账务如何处理缓冲流量,削峰填谷+分布式事物的
目的:解决业务定时任务量的削峰填谷;安全一致性(最大化消息不丢);

RabbitMQ解决分布式事务原理: 采用最终一致性原理。需要保证以下要素

1、幂等性、请求不重复,redis分布式锁()
2、确认生产者投递,一定要将数据投递到MQ服务器中(采用MQ发送消息确认机制
3、确认MQ消费者消费,消息能够正确消费,采用手动ACK模式(注意重试幂等性问题,不要依赖队列重试,走业务代码重试)
4、死信队列,消息重试失败3次,告警,放入死信队列()
6、通知机制(补偿通知机制),
真实项目举例2:
账务系统
1.redis分布式锁,防止相同订单并发。然后校验基本参数,对请求号(还有业务订单号,如果重复了,队列消费时校验批次所有订单号是否完全幂等,是就成功)下单入库,初始状态(要不要先把普通用户钱够了或所有形成记账数据包再扔队列,最好整体扔队列安全)。
2. 确认机制保证提交到队列。把消息发往队列,这里做成近似一致性:重写业务生产者发送消息确认机制,并把CorrelationData 类重写了,增加消息体字段和计数字段,用来重发和失败计数,如果正常,保留原来机制,什么都不做,如果ack失败,计数+1,等2秒并重发,如果3次了,报警,人工干预(可能是队列有问题),在这里先扔死信队列1(消费失败也是扔这里)。(再看一下建消息表+kafka的弊端,它会在发送成功之后有个删DB消息表的操作,还要定时任务去把消息表未发送成功状态的消息读出来重发,效率慢)
3. (下游模块被消费)账务系统的消费模块要保证操作成功记账才ack队列。消费模块监听队列,采用手动ACK模式,(一定不要抛异常,不要依赖队列重试,拿不到重试次数,除非想办法设置),消费模块的业务记账块封装成可重试执行块,代码重试超过3次未知异常失败(余额不足不算,比如超时),就告警,并Nack该消息,进入死信队列1,待人工处理(业务逻辑有问题或超时3次)。

3.2 另外,成功记账后得在同事物内,更改请求表状态为记账成功或失败,(通知状态和业务状态是两字段)
6、通知上层调用方系统。立马通知一次上层系统,如果通知失败,进入补偿通知机制
7、补偿通知机制(延时队列,间隔性通知)。(首先会提供一个主动查询请求号是否成功的接口给上层系统用)。
任务调度策略一般设定3s、30s、60s、10分钟调度多次。
参考自已写的:面试整理-3-RabbitMQ的  ?RabbitMQ如何实现定时阶梯性通知?
 一、选延时队列来拓展。(建意用)
延时队列本身是没有消费者的(直拉拒绝的意思),设置TTL放进去之后,需要将其DLX绑定到其他普通队列2队列2监听,队列1的时间到之后,队列2就会收到消息,然后通知一次,若失败重置TTL到期(生存)时间,假设当前3秒,那重新发布一条60秒进延时队列。如此3秒,60秒,10分,执行到10分这次停止,我就不再重放队列了。等业务人工补偿,邮件告警

参考 :https://blog.csdn.net/skiof007/article/details/80914318

此处用方法1的延时队列,修改header里面的Map参数值:
original-expiration这个字段表示消息在原来队列中的过期时间,根据这个值来确定下一次通知的延迟时间应该是多少秒。
x-dead-letter-exchange

同步的批量交易接口超时严重(保留同步幂等的情况下,改用异步通知,异步的好处:1.支付结果更准确安全2.用服务器接收可以在后续做各种业务逻辑。异步机制:0重试一次,1,30,2小时,10小时,次数存redis)
 

最后实现一个系统模式:接口同步返回受理成功,异步通知+主动补偿查询的补偿机制。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章