分布式事务 笔记

为什么使用分布式事务

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

本地事务

事务特性(ACID)

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

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

事务隔离级别

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

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

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

imagepng

事务传播

事务传播行为类型说明
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也要回滚。
imagepng

  • 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事务仍然可能提交。
imagepng

  • 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>

imagepng
在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
imagepng
imagepng
分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免
主流的解决方案如下:

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

分布式事务模型(规范)

两阶段提交(2PC)

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

三阶段提交(3PC)

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

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

3PC中的超时策略
imagepng
参与者等待 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阶段真的出错了,需引入重试机制或人工处理。
另一个服务也是这样,只用于理解
另一个fegin

imagepng
**第一阶段:**主业务服务分别调用所有从业务的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账户就不做操作。
imagepng
缺点:

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

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

可靠消息最终一致性方案

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

最大努力通知方案

imagepng
imagepng
交互流程:
账户系统调用充值系统接口
充值系统完成支付处理向账户系统发起充值结果通知 若通知失败,则充值系统按策略进行重复通知
账户系统接收到充值结果通知修改充值状态。
账户系统未接收到通知会主动调用充值系统的接口查询充值结果
具体包括:
有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。
消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息 信息来满足需求。
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、技术解决方向不同
可靠消息一致性要解决消息从发出到接收的一致性,即消息发出并且被接收到。
最大努力通知无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠机制是,最大努力的将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息(业务处理结果)。
imagepng
imagepng
实例

分布式事务服务框架

seata

seata-server服务的安装

imagepng

集成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
  }
}

imagepng
imagepng
imagepng
imagepng

imagepng

ByteTCC

tcc-transaction

himly

取消订单

取消订单出现的情况:

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

实现方式:定时任务和消息中间件,定时任务对系统的性能肯定是有影响
imagepng
imagepng
imagepng
imagepng

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
    

    imagepng

  • 安装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
    

    imagepng
    imagepng

  • 安装控制台

    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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/367740.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机服务器中了DevicData勒索病毒如何解密,DevicData勒索病毒解密流程

网络数据安全一直是企业关心的主要话题&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机服务器遭到了DevicData勒索病毒攻击&#xff0c;导致企业计算机服务器瘫痪无法正常工作&#xff0c;严重影响了工作业务开展。经过云天数据恢复中…

debian12 解决 github 访问难的问题

可以在 /etc/hosts 文件中添加几个域名与IP对应关系&#xff0c;从而提高 github.com 的访问速度。 据搜索了解&#xff08;不太确定&#xff09;&#xff0c;可以添加这几个域名&#xff1a;github.com&#xff0c;github.global.ssl.fastly.net&#xff0c;github.global.fa…

计算机组成原理(0)冯诺依曼体系结构

文章目录 定义**主要特点&#xff1a;****缺陷&#xff1a;** 定义 冯诺依曼体系结构&#xff08;Von Neumann architecture&#xff09;&#xff0c;也称为普林斯顿体系结构&#xff08;Princeton architecture&#xff09;&#xff0c;是一种计算机架构理论&#xff0c;由匈…

在centos 7 中 安装 配置 并 远程连接 MySQL5.7

目录 安装MySQL 1.卸载CentOS7系统自带的mariadb 2.安装依赖库 3.上传MySQL并解压 4.安装MySQL 配置MySQL 1.修改登录密码 2.修改字符集 3.配置远程连接 前言&#xff1a; 安装MySQL版本&#xff1a;mysql-5.7.30-1.el7.x86_64.rpm-bundle 文件需求后台私信 以下7条为…

【C语言】数组的应用:扫雷游戏(包含扩展和标记功能)附完整源代码

这个代码还是比较长的&#xff0c;为了增加可读性&#xff0c;我们还是把他的功能分装到了test.c&#xff0c;game.c&#xff0c;game.h里面。 扫雷游戏的规则相信大家来阅读本文之前已经知晓了&#xff0c;如果点到雷就输了&#xff0c;如果不是雷&#xff0c;点到的格子会显…

红队渗透靶机:LORD OF THE ROOT: 1.0.1

目录 信息收集 1、arp 2、nmap 3、knock 4、nikto 目录探测 1、gobuster 2、dirsearch WEB sqlmap 爆库 爆表 爆列 爆字段 hydra爆破 ssh登录 提权 信息收集 内核提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, ty…

十年饮冰难凉热血——HTX重塑巴别塔

明天将会是不同的世界&#xff0c;该由不同的人来塑造。 2024年1月18日&#xff0c;HTX DAO正式成立。 作为区块链生态系统中领先的去中心化自治组织&#xff0c;HTX DAO以创新的治理方式&#xff0c;专注于开放金融和去中心化的代币化经济。 HTX DAO是一个富有远见和包容性…

基于springboot企业客户信息反馈平台源码和论文

网络的广泛应用给生活带来了十分的便利。所以把企业客户信息反馈管理与现在网络相结合&#xff0c;利用java技术建设企业客户信息反馈平台&#xff0c;实现企业客户信息反馈的信息化。则对于进一步提高企业客户信息反馈管理发展&#xff0c;丰富企业客户信息反馈管理经验能起到…

问题:在下列选项中,下列哪种情况不属于生理排泄过程的是() #媒体#学习方法#经验分享

问题&#xff1a;在下列选项中&#xff0c;下列哪种情况不属于生理排泄过程的是&#xff08;&#xff09; A.CO2由呼吸系统排出 B.食物残渣由消化道排出 C.皮肤排出汗液 D.肾脏排出尿液 E.由消化道排出的胆色素 参考答案如图所示

Leetcode的AC指南 —— 栈与队列 :1047.删除字符串中的所有相邻重复项

摘要&#xff1a; **Leetcode的AC指南 —— 栈与队列 &#xff1a;1047.删除字符串中的所有相邻重复项 **。题目介绍&#xff1a;给出由小写字母组成的字符串 S&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 S 上反复执行重复项删除操作&a…

高宇辰:打造“π”型人才 | 提升之路系列(七)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

抽象类(Java)、模板方法设计模式

一、概念 在Java中有abstract关键字&#xff0c;就是抽象的意思&#xff0c;可用来修饰类和成员方法。 用abstract来修饰类&#xff0c;那这个类就是抽象类&#xff1b;修饰方法&#xff0c;那这个方法就是抽象方法。 修饰符 abstract class 类名{修饰符 abstract 返回值类型…

故障诊断 | 一文解决,BiLSTM双向长短期记忆神经网络故障诊断(Matlab)

文章目录 效果一览文章概述专栏介绍模型描述源码设计参考资料效果一览 文章概述 故障诊断模型 | Maltab实现BiLSTM双向长短期记忆神经网络故障诊断 专栏介绍 订阅【故障诊断】专栏,不定期更新机器学习和深度学习在故障诊断中的应用;订阅

List集合接口的介绍和使用

一.关于List集合类的继承关系图 List接口继承了Collection接口&#xff0c;而List接口下有三个重要的实现类:ArrayList&#xff0c;LinkedList&#xff0c;Vector 二.List接口的基本介绍 1.List接口是Collection接口的子接口2.存入List集合中的元素是有序的&#xff08;即添加…

面试经典150题——文本左右对齐(困难)

​"It always seems impossible until it’s done." - Nelson Mandela 1. 题目描述&#xff1a; 这个题目标为困难题目&#xff0c;但是如果我们静下心来把题目读懂了&#xff0c;其实无非就是不同情况下不同考虑而已&#xff0c;也没什么思维上的复杂&#xff0c;还…

银行数据仓库体系实践(8)--主数据模型设计

主数据区域中保留了数据仓库的所有基础数据及历史数据&#xff0c;是数据仓库中最重要的数据区域之一&#xff0c;那主数据区域中主要分为近源模型区和整合&#xff08;主题&#xff09;模型区。上一节讲到了模型的设计流程如下图所示。那近源模型层的设计在第2.3和3这两个步骤…

微信积分系统怎么做_开启用户忠诚度之门

积分系统&#xff1a;开启用户忠诚度之门 在数字化时代&#xff0c;积分系统已经成为了企业与消费者之间互动的桥梁。它不仅是一种奖励机制&#xff0c;更是提升用户忠诚度、促进消费的重要手段。本文将深入探讨如何将积分系统作为主题&#xff0c;撰写一篇高质量的营销软文&a…

记录element-plus树型表格的bug

问题描述 如果数据的子节点命名时children,就没有任何问题&#xff0c;如果后端数据结构子节点是其他名字&#xff0c;比如thisChildList就有bug const tableData [{id: 1,date: 2016-05-02,name: wangxiaohu,address: No. 189, Grove St, Los Angeles,selectedAble: true,th…

Socket通信之获取服务器端文件列表点击下载

客户端读取服务器端的文件目录,自主选择进行下载。(AS实现) 1.Manifest添加权限 与之前博文相同,不再赘述。详见: Socket通信-CSDN博客文章浏览阅读272次,点赞4次,收藏10次。套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。socket通…

乘方计算 T1062

#include<bits/stdc.h> using namespace std; int a,n, power1; int main(){cin>>a>>n;for(int i1;i<n;i){power*a;}cout<<power<<endl;return 0; }