分布式事务 笔记

为什么使用分布式事务

分布式环境下一个业务可能会涉及到多个模块之间的调用,为了保证操作的原子性,分布式事务是最好的解决方案。
假设会员服务异常,这是已经完成锁库,锁库无法回滚。
分布式事务 笔记_第1张图片

本地事务

事务特性(ACID)

  1. 事务的概念:事务是逻辑上/一组操作,组成这组操作各个逻辑单元,要么一起成功,要么一起失败。
  2. 事务的四个特性(ACID)

原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
一致性(consistency):一致指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。
隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰
持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。

事务隔离级别

  1. 事务并发引起一些读的问题

脏读:一个事务可以读取另一个事务未提交的数据;
不可重复读:一个事务可以读取另一个事务已提交的数据 单条记录前后不匹配;
幻读::一个事务可以读取另一个事务已提交的数据 读取的数据前后多了点或者少了点

  1. 并发写:使用mysql默认的锁机制(独占锁)
  2. 解决读问题:设置事务隔离级别

分布式事务 笔记_第2张图片

事务传播

事务传播行为类型 说明
PROPAGATION_REQUIRED (默认事务)需要事务。若当前无事务,新建一个事务;若当前有事务,加入此事务中。
PROPAGATION_SUPPORTS 支持事务。若当前没有事务,以非事务方式执行;若当前有事务,加入此事务中。
PROPAGATION_MANDATORY 强制使用事务。若当前有事务,就使用当前事务;若当前没有事务,抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务。无论当前是否有事务,都新建事务运行。
PROPAGATION_NOT_SUPPORTED 不支持事务。若当前存在事务,把当前事务挂起,然后运行方法。
PROPAGATION_NEVER 不使用事务。若当前方法存在事务,则抛出异常,否则继续使用无事务机制运行。
PROPAGATION_NESTED 嵌套。如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

实例

ServiceA {   
     void methodA() {
         ServiceB.methodB();
     }
}

ServiceB { 
     void methodB() {
     }  
}
  • PROPAGATION_REQUIRED

ServiceB.methodB的事务级别定义PROPAGATION_REQUIRED, 那么因为执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务。这时调用ServiceB.methodB,ServiceB.methodB看到自己已经执行在ServiceA.methodA的事务内部。就不再起新的事务。而假如ServiceA.methodA执行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的不论什么地方出现异常。事务都会被回滚。即使ServiceB.methodB的事务已经被提交,可是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。
分布式事务 笔记_第3张图片

  • PROPAGATION_SUPPORTS
  • PROPAGATION_MANDATORY
  • PROPAGATION_REQUIRES_NEW

设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW。那么当运行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起。ServiceB.methodB会起一个新的事务。等待ServiceB.methodB的事务完毕以后,他才继续运行。 他与PROPAGATION_REQUIRED 的事务差别在于事务的回滚程度了。由于ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。假设ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚。ServiceB.methodB是不会回滚的。假设ServiceB.methodB失败回滚,假设他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
分布式事务 笔记_第4张图片

  • PROPAGATION_NOT_SUPPORTED

当前不支持事务。比方ServiceA.methodA的事务级别是PROPAGATION_REQUIRED 。而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时。ServiceA.methodA的事务挂起。而他以非事务的状态执行完,再继续ServiceA.methodA的事务。

  • PROPAGATION_NEVER

不能在事务中执行。 如果ServiceA.methodA的事务级别是PROPAGATION_REQUIRED。 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。

  • PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

SpringBoot事务代理对象

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.4.12</version>
</dependency>

分布式事务 笔记_第5张图片
在service中调用自身的其他事务方法的时候,事务的传播行为会失效

/**
 * 在service中调用自身的其他事务方法的时候,事务的传播行为会失效
 *   因为会绕过代理对象的处理
 *
 */
@Transactional // 事务A
public void a(){
    OrderServiceImpl o = (OrderServiceImpl) AopContext.currentProxy();
    o.b(); // 事务A
    o.c(); // 事务C
    int a = 10/0;
}

@Transactional(propagation = Propagation.REQUIRED)
public void b(){

}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void c(){

}

分布式事务

分布式事务基础

CAP定理

分布式存储系统的CAP原理(分布式系统的三个指标):

  • Consistency(一致性):在分布式系统中的所有数据备份,在同一时刻是否同样的值。对于数据分布在不同节点上的数据来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
  • Availability(可用性):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(要求数据需要备份)
  • Partition tolerance(分区容忍性):大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。

CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们无法避免的。所以我们只能在一致性和可用性之间进行权衡,没有系统能同时保证这三点。要么选择CP、要么选择AP。

BASE定理

BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

  • Basically Available(基本可用)

基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。

  • Soft state(软状态)

软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。

  • Eventually consistent(最终一致性)

最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。
BASE模型是传统ACID模型的反面,不同于ACID,BASE强调牺牲高一致性,从而获得可用性,数据允许在一段时间内的不一致,只要保证最终一致就可以了。

分布式事务的解决方案

https://www.processon.com/view/link/62a1ddce0791293ad1a552c0
分布式事务 笔记_第6张图片
分布式事务 笔记_第7张图片
分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免
主流的解决方案如下:

  1. 基于XA协议的两阶段提交(2PC)
  2. 柔性事务-TCC事务
  3. 柔性事务-最终一致性

分布式事务模型(规范)

两阶段提交(2PC)

分布式事务 笔记_第8张图片
2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。
举例:张三和李四好久不见,老友约起聚餐,饭店老板要求先买单,才能出票。这时张三和李四分别抱怨近况不如意,囊中羞涩,都不愿意请客,这时只能AA。只有张三和李四都付款,老板才能出票安排就餐。
准备阶段:老板要求张三付款,张三付款。老板要求李四付款,李四付款。
提交阶段:老板出票,两人拿票纷纷落座就餐。
例子中形成了一个事务,若张三或李四其中一人拒绝付款,或钱不够,店老板都不会给出票,并且会把已收款退回。整个事务过程由事务管理器和参与者组成,店老板就是事务管理器,张三、李四就是事务参与者,事务管理器负责决策整个分布式事务的提交和回滚,事务参与者负责自己本地事务的提交和回滚。
在计算机中部分关系数据库如Oracle、MySQL支持两阶段提交协议,如下图:
1) 准备阶段(Prepare phase):事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。(Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数据文件)
2) 提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。
分布式事务 笔记_第9张图片

三阶段提交(3PC)

分布式事务 笔记_第10张图片
3PC(Three-Phase Commit)是一种分布式系统中用于实现事务一致性的协议,它是在2PC(Two-Phase Commit)的基础上发展而来,旨在解决2PC的一些缺点。与2PC的两个阶段(准备和提交)相比,3PC引入了一个额外的阶段,即预提交阶段。
分布式事务 笔记_第11张图片
3PC的出现
2PC 协议存在的协调者单点、参与者阻塞超时、网络分区、容错性等问题,在协调者和唯一接收指令的参与者都出现不可恢复宕机时,即使后面选举了新的协调者,仍然可能出现数据的不一致性。
分布式事务 笔记_第12张图片
3PC协议的基本过程
分布式事务 笔记_第13张图片
3PC的三个阶段:
CanCommit(准备阶段):
在这个阶段,协调者向所有的事务参与者询问是否可以提交事务。每个参与者要么发送“同意”消息,表示可以提交,要么发送“中止”消息,表示不能提交。
在2PC准备阶段中,协调者向参与者发送指令后,参与者如果具备执行条件,则获取锁并执行动作,只不过未真正提交,可以认为参与者就差临门一脚了,还得等协调者信号。
3PC来说更加合理,先由协调者向参与者发送询问信号,兄弟们有档期吗??然后开始收集反馈,相比来说更加轻量。这个阶段参与者并不真实获取锁占用资源,只是对自身执行事务状态的检查,查看是否具备执行事务的条件,进而回复询问。
分布式事务 笔记_第14张图片
分布式事务 笔记_第15张图片

PreCommit(预提交阶段):
如果所有的参与者都发送了“同意”消息,协调者将向所有的参与者发送“预提交”消息,通知它们准备提交事务。参与者会在本地执行预提交操作,但是还不会真正提交。
第二个阶段的具体动作取决于第一个阶段的结果,因此可以分为两种情况:
在第一阶段所有参与者都 Ready,那么协调者就会向参与者发送本地执行的相关指令,这部分和 2PC 的第一阶段非常相似,参与者收到指令后进行本地事务执行,并记录日志,并且对处理结果反馈到协调者,来做决策。过程中参与者可能成功或者失败,出现了两种情况:
CanCommit阶段一致通过:
分布式事务 笔记_第16张图片
分布式事务 笔记_第17张图片
CanCommit阶段存在分歧:
在第一阶段如果存在参与者不 Ready 的情况,那么协调者就会发送信号给所有参与者,告知本次事务取消了,该干啥干啥吧,对参与者来说损失并不大,因为本质上参与者并没有做什么事情。
分布式事务 笔记_第18张图片
DoCommit(提交阶段):
如果在预提交阶段没有发生错误,协调者将向所有的参与者发送“提交”消息,要求它们最终提交事务。如果有任何参与者在预提交阶段发生了错误,协调者将发送“中止”消息,要求所有的参与者回滚事务。
3PC 的第三个阶段和2PC的第二个阶段类似,同样的 DoCommit 执行具体动作取决于第二阶段 PreCommit 的结果,因此仍然分为两种情况:
在 PreCommit 之后参与者全部完成本地事务执行但是没有提交,并且都给协调者 ACK 回复,这时协调者认为万事俱备只欠东风了,在 DoCommit 阶段协调者向参与者发送提交指令,参与者收到之后开始执行本地提交,并反馈结果,最终完成这次事务。
分布式事务 笔记_第19张图片
协调者在第二个阶段 PreCommit 收到参与者的反馈后,发现存在部分参与者无法执行事务的情况,这时就决定告诉其他参与者本地回滚,释放资源,取消本次事务。
分布式事务 笔记_第20张图片

3PC中的超时策略
分布式事务 笔记_第21张图片
参与者等待 PreCommit 超时:
协调者和参与者之间可能存在较大的网络延时,或者协调者出现故障,或者出现网络分区等情况,参与者并不会傻等,在超过设定时间之后,参与者就继续做之前的事情了
参与者等待 DoCommit 超时:
在 CanCommit 和 PreCommit 之后,参与者认为大家都对齐了都是棒棒的,如果参与者在设定时间内并没有收到协调者的 DoCommit 指令,那么就本地执行提交完成这次事务,因为参与者揣测大哥的意思大概率也是让我们提交,干等着也不是办法,回滚可能和大家不一样,抉择之下参与者选择提交事务。
协调者等待反馈超时:
3PC协调者的等待超时处理和2PC基本上是一样的,无论在哪个阶段超时都认为不具备条件,进行 abort 或者 rollback 操作

分布式事务解决方案

TCC补偿式事务

imagepng
TCC(Try/Confirm/Cancel)编程模式的核心思想是:针对每个分支事务操作,都要向全局事务发起方注
册Try、Confirm和Cancel三个操作,具体这些操作由我们自己根据业务进行实现,然后分为两个阶段去
执行:
Try 阶段主要是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm 一起才能真正构成一个完整的业务逻辑。
Confirm 阶段主要是做确认提交,Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。
Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。
分布式事务 笔记_第22张图片
分布式事务 笔记_第23张图片

分布式事务 笔记_第24张图片
**第一阶段:**主业务服务分别调用所有从业务的try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败,进入第二阶段。
**第二阶段:**活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功,则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作。
TCC补偿事务常见的是转账案例
首先存在两个对象,账户A和账户B,A欲向B转账money,A是事务发起者,
A账户在:
try阶段:首先检查可用余额是否满足money,其次冻结金额money,最后扣除money。
confirm阶段:解冻金额money
cancel阶段:这个阶段是取消事务后的操作,try or cancel,那么这个阶段就应该:解冻金额money,增加可用金额money
B账户在
try阶段:检查账户状态
confirm阶段:余额增加money
cancel阶段:这个阶段对应事务发起者(账户A的cancel阶段事务)如果A事务没完成转账,那么此时B账户就不做操作。
分布式事务 笔记_第25张图片
缺点:

  • Canfirm和Cancel的幂等性很难保证。
  • 这种方式缺点比较多,通常在复杂场景下是不推荐使用的,除非是非常简单的场景,非常容易提供回滚Cancel,而且依赖的服务也非常少的情况。
  • 这种实现方式会造成代码量庞大,耦合性高。而且非常有局限性,因为有很多的业务是无法很简单的实现回滚的,如果串行的服务很多,回滚的成本实在太高。

不少大公司里,其实都是自己研发 TCC 分布式事务框架的,专门在公司内部使用。国内开源出去的:ByteTCC,TCC-transaction,Himly。

可靠消息最终一致性方案

分布式事务 笔记_第26张图片
基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。
分布式事务 笔记_第27张图片
虽然上面的方案能够完成A和B的操作,但是A和B并不是严格一致的,而是最终一致的,我们在这里牺牲了一致性,换来了性能的大幅度提升。当然,这种玩法也是有风险的,如果B一直执行不成功,那么一致性会被破坏。
适用于高并发最终一致
低并发基本一致:二阶段提交
高并发强一致:没有解决方案
分布式事务 笔记_第28张图片
消费者消费消息失败,或者消费成功但执行本地事务失败。针对这种情况,可靠消息服务可以提供一个后台定时任务,不停的检查消息表中那些【已发送】但始终没有变成【已完成】的消息,然后再次投递到MQ,让下游服务来再次处理。也可以引入zookeeper,由消费者通知zookeeper,生产者监听到zookeeper上节点变化后,进行消息的重新投递。
如果消息重复投递,消息者的接口逻辑需要实现幂等性,保证多次处理一个消息不会插入重复数据或造成业务数据混乱。针对这种情况,消费者可以准备一张消息表,用于判重。消费者消费消息后,需要去本地消息表查看这条消息有没处理成功,如果处理成功直接返回成功。

最大努力通知方案

分布式事务 笔记_第29张图片
分布式事务 笔记_第30张图片
交互流程:
账户系统调用充值系统接口
充值系统完成支付处理向账户系统发起充值结果通知 若通知失败,则充值系统按策略进行重复通知
账户系统接收到充值结果通知修改充值状态。
账户系统未接收到通知会主动调用充值系统的接口查询充值结果
具体包括:
有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。
消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息 信息来满足需求。
https://wenku.baidu.com/view/ac1058e90608763231126edb6f1aff00bed5709f.html?wkts=1705335130629&bdQuery=%E6%9C%80%E5%A4%A7%E5%8A%AA%E5%8A%9B%E9%80%9A%E7%9F%A5%E6%96%B9%E6%A1%88
最大努力通知与可靠消息一致性有什么不同?
1、解决方案思想不同
可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。
最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。
2、两者的业务应用场景不同
可靠消息一致性关注的是交易过程的事务一致,以异步的方式完成交易。
最大努力通知关注的是交易后的通知事务,即将交易结果可靠的通知出去。
3、技术解决方向不同
可靠消息一致性要解决消息从发出到接收的一致性,即消息发出并且被接收到。
最大努力通知无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠机制是,最大努力的将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息(业务处理结果)。
分布式事务 笔记_第31张图片
分布式事务 笔记_第32张图片
分布式事务 笔记_第33张图片

分布式事务服务框架

seata

seata-server服务的安装

分布式事务 笔记_第34张图片

集成Seata
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    serverAddr = "192.168.56.10:8848"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = 0
    password = ""
    cluster = "default"
    timeout = 0
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    appId = "seata-server"
    apolloMeta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}
## 当前微服务在seata服务器中注册的信息配置
service {
  # 事务分组,默认:${spring.applicaiton.name}-fescar-service-group,可以随便写
  vgroupMapping.mall-order-group = "default"
  # 仅支持单节点,不要配置多地址,这里的default要和事务分组的值一致
  default.grouplist = "192.168.0.102:8091" #seata-server服务器地址,默认是8091
  # 降级,当前不支持
  enableDegrade = false
  # 禁用全局事务
  disableGlobalTransaction = false
}
## transaction log store, only used in seata-server
## 事务日志存储配置:该部分配置仅在seata-server中使用,如果选择db请配合seata.sql使用
store {
  ## store mode: file、db、redis
  mode = "file"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "mysql"
    password = "mysql"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

  ## redis store property
  redis {
    host = "127.0.0.1"
    port = "6379"
    password = ""
    database = "0"
    minConn = 1
    maxConn = 10
    queryLimit = 100
  }

}
## 网络传输配置
transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
}

## 客户端相关工作的机制
client {
  rm {
    async.commit.buffer.limit = 10000
    lock {
      retry.internal = 10
      retry.times = 30
      retry.policy.branch-rollback-on-conflict = true
    }
    report.retry.count = 5
    table.meta.check.enable = false
    report.success.enable = true
  }
  tm {
    commit.retry.count = 5
    rollback.retry.count = 5
  }
  undo {
    data.validation = true
    log.serialization = "jackson"
    log.table = "undo_log"
  }
  log {
    exceptionRate = 100
  }
  support {
    # auto proxy the DataSource bean
    spring.datasource.autoproxy = false
  }
}

分布式事务 笔记_第35张图片
分布式事务 笔记_第36张图片
分布式事务 笔记_第37张图片
分布式事务 笔记_第38张图片

分布式事务 笔记_第39张图片

ByteTCC

tcc-transaction

himly

取消订单

取消订单出现的情况:

  • 下订单后超过30分钟没有支付,需要触发关单操作
  • 支付失败,同样的需要关单

实现方式:定时任务和消息中间件,定时任务对系统的性能肯定是有影响
分布式事务 笔记_第40张图片
分布式事务 笔记_第41张图片
分布式事务 笔记_第42张图片
分布式事务 笔记_第43张图片

RocketMQ:Docker安装
  • 安装NameServer

    docker pull rocketmqinc/rocketmq
    
    mkdir -p /mydata/docker/rocketmq/data/namesrv/logs /mydata/docker/rocketmq/data/namesrv/store
    
    docker run -d --restart=always --name rmqnamesrv --privileged=true -p 9876:9876  -v /mydata/docker/rocketmq/data/namesrv/logs:/root/logs -v /mydata/docker/rocketmq/data/namesrv/store:/root/store -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq sh mqnamesrv
    

    分布式事务 笔记_第44张图片

  • 安装Broker

    # 所属集群名称,如果节点较多可以配置多个
    brokerClusterName = DefaultCluster 
    #broker名称,master和slave使用相同的名称,表明他们的主从关系 
    brokerName = broker-a 
    #0表示Master,大于0表示不同的
    slave brokerId = 0 
    #表示几点做消息删除动作,默认是凌晨4点 
    deleteWhen = 04 
    #在磁盘上保留消息的时长,单位是小时 
    fileReservedTime = 48 
    #有三个值:SYNC_MASTERASYNC_MASTERSLAVE;同步和异步表示MasterSlave之间同步数据的机 制;
    brokerRole = ASYNC_MASTER 
    #刷盘策略,取值为:ASYNC_FLUSHSYNC_FLUSH表示同步刷盘和异步刷盘;SYNC_FLUSH消息写入磁盘后 才返回成功状态,ASYNC_FLUSH不需要;
    flushDiskType = ASYNC_FLUSH 
    # 设置broker节点所在服务器的ip地址 
    brokerIP1 = 192.168.100.10
    #剩余磁盘比例 
    diskMaxUsedSpaceRatio=99
    
    docker run -d --restart=always --name rmqbroker --link rmqnamesrv:namesrv -p 10911:10911 -p 10909:10909 --privileged=true -v /mydata/docker/rocketmq/broker/logs:/root/logs -v /mydata/docker/rocketmq/broker/store:/root/store -v /mydata/docker/rocketmq/broker/conf/broker.conf:/opt/rocketmq-4.4.0/conf/broker.conf -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/rocketmq-4.4.0/conf/broker.conf
    

    分布式事务 笔记_第45张图片
    分布式事务 笔记_第46张图片

  • 安装控制台

    docker pull pangliang/rocketmq-console-ng
    
    docker run -d --restart=always --name rmqadmin -e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.56.10:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false -Duser.timezone='Asia/Shanghai'" -v /etc/localtime:/etc/localtime -p 8080:8080 pangliang/rocketmq-console-ng
    

释放库存

需要释放库存的情况:

  • 下订单后手动取消订单获取超时支付订单
  • 支付成功释放库存,更新库存

SpringBoot整合RocketMQ

你可能感兴趣的:(分布式,笔记)