https://www.jianshu.com/p/c26b3af5880f
https://www.jianshu.com/p/fc47ebaff6a4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
微服务倡导将复杂的系统拆分为若干个简单、职责单一、松耦合的服务,可以降低开发难度,便于敏捷开发。而对大多数中小型公司来说,实施微服务架构面临以下困难:
为了保障微服务架构下数据的一致性,通常需要引入分布式事务来解决,当前比较流行的分布式解决方案如下。
基于二阶段提交的XA协议
第二阶段:若所有参与者回应“可提交”,则向所有参与者发送正式提交命令;若某个参与者回应“拒绝提交”,则向所有参与者发送回滚命令。
image.png
XA协议保障了事务的强一致性,然而由于其采用的阻塞协议带来的巨大性能开销,难以达到较高的系统吞吐量。
TCC提供了一种全局事务解决方案,业务系统只需实现下面三个操作,即可完成分布式事务:
CANCEL:释放TRY结算预留的业务资源
image.png
TCC模式可以让业务更灵活地定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能,然而它对业务的侵入度较高,实现难度较大。
通过消息的异步事务,可以保证本地事务和消息发送同时执行成功或失败,从而保证了数据的最终一致性。
若MQ长时间未收到确认发送或取消发送的指令,则向业务系统询问本地事务状态,并做补偿处理
image.png
image.png
客户端事务消息发送
image.png
发送prepare消息复用了普通消息发送,只是给消息增加了TRAN_MSG=true的属性,该属性决定prepare消息对Consumer不可见
消息写入CommitLog
image.png
事务消息的CommitLog写入和普通消息一致,它利用文件的顺序写来提升吞吐量,并采用文件映射的方式降低系统开销。
消息写入ConsumeQueue
image.png
ConsumeQueue的写入是采用异步方式完成的,ReputMessageSerivce作为一个长驻线程负责查询索引的构造和ConsumeQueue的写入,对于Prepare/Rollback消息不会写ConsumeQueue,从而保证它们对Consumer不可见
Broker端事务提交/回滚
image.png
Broker收到提交/回滚指令后,首先从根据offset从CommitLog读出原有的prepare消息,构造新的消息(修改事务状态标识)并写入Broker。对于一条事务消息,RocketMq会存储两条消息,存在一定资源浪费。其实它是为了保证随后的消费者能尽可能从PageCache中读到该消息,而不是读取较早的prepare消息(可能导致缺页中断),以提升系统吞吐量。
此外,rocketmq的最新版本(4.2.0)尚未支持本地事务的状态回查,这样可能存在由于网络抖动,导致commit/rollback未提交到broker导致prepare消息长期悬挂的风险。
在RocketMq的设计文档中,为事务消息增加了一张事务状态表,它维护了消息的Offset、事务状态(P/C/R)信息。可以采用如下思路实现事务消息的回查机制:
image.png
=================================
rocketMq在4.x版本里面的事务消息功能似乎不是完整的,在阅读的源码过程中发现只有其实宏观上看可以区分为两个阶段
阶段一:prepare阶段,该阶段主要发一个消息到rocketMq,但是该消息只存储在commitLog当中,在consumeQueue中不可见。
阶段二:commit/rollback阶段,该阶段主要是在commit阶段重新生成一个message(prepare阶段消息的克隆),保存到commitLog当中,同时会将该消息保存至consumeQueue当中。
分布式事务的场景
rocketMq处理分布式消息的过程
说明:
1、先发送prepared消息,也就是步骤1中的发送转账消息
2、执行本地操作,也就是步骤4中的本地事务
3、发送commit消息,也就是步骤中5中的发送确认消息
rocketMq分布式事务的过程
说明:
1、参考阿里云的消息队列截图
事务性消息生产者
说明:
1、分布式事务producer多了两个东西,本地事务执行函数,提供给远程回调的回调函数(基本没用)。
prepare消息
说明:参见DefaultMQProducerImpl类
未拷贝至consumeQueue
说明:参见DefaultMessageStore类
1、prepare消息不回拷贝至consumeQueue,也就是消息不可见。
commit阶段查找prepare消息并提交commitLog
说明:参见EndTransactionProcessor类。
1、EndTransactionProcessor是事务消息commit的入口
放置consumeQueue
说明:参见DefaultMessageStore类
1、rocketMq在消费commit的事务消息的时候会放置到consumeQueu当中
2、放置到consumeQueue以后就可以被消费了