一、背景

RocketMQ的分布式事务可以称为“半消息事务”。

二、原理

2.1原理

RocketMQ是靠半消息机制实现分布式事务:

事务消息:MQ 提供类似 X/Open XA 的分布事务功能,通过 MQ 事务消息能达到分布式事务的最终一致。
半消息:暂不能投递的消息,发送方已经将消息成功发送到了 MQ 服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半消息。
半消息回查:由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,MQ 服务端通过扫描发现某条消息长期处于“半消息”时,需要主动向消息生产者询问该消息的最终状态(Commit 或是 Rollback),该过程即消息回查。

流程:

1.发送方向 MQ 服务端发送事务消息;

2.MQ Server 将消息持久化成功之后,向发送方 ACK 确认消息已经发送成功,此时消息为半消息。

3.发送方开始执行本地事务逻辑。

4.发送方根据本地事务执行结果向 MQ Server 提交二次确认(Commit 或是 Rollback),MQ Server 收到 Commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 Rollback 状态则删除半消息,订阅方将不会接受该消息。

5.在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。

6.发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。

7.发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤4对半消息进行操作。

2.2 疑问

极端情况:

是否任何情况下MQ的事务性消息都可以保证双方的最终一致性?答案是否定的。

考虑上面提到的异常情况“情况2:MQ发送方在步骤(3)执行完本地事务之后commit之前异常退出”。在这种情况下如果如果MQ发送方由于运维上的失误长时间不重启MQ发送方,那么MQ在多次回查不成功之后将会丢弃该消息。最终分布式事务的双方是不能达到最终一致性了。当然这个回查的最大值可以通过修改broker的参数transactionCheckMax来调整。但是过大的transactionCheckMax参数将会导致MQ堆积过多的半包消息,从而危害MQ的稳定性,是个需要权衡的参数。

三、使用

如上图所示,使用者只需要实现紫色+绿色模块:

  • 紫色代表业务方自定义实现,
  • 绿色代表RocketMQ定义业务需要实现的方法。

具体步骤如下:

一、生产者

1.业务方保存本地事务记录,并初始化状态。

2.业务方调用sendMessageInTransaction发送半消息到MQ的RMQ_SYS_TRANS_HALF_TOPIC队列。

3.MQ执行成功,回调业务方executeLocalTransaction方法,也就是业务方的业务逻辑。

4.业务方返回事务状态给MQ,

  1. commit: 塞一条消息进REAL_TOPIC真实队列,等待消费者消费。
  2. commit/rollback:添加一条消息进RMQ_SYS_TRANS_OP_HALF_TOPIC队列,代表已处理消息。
  3. unknow:根据一定的频率回查业务方本地事务状态。

5.MQ内部有定时任务,轮询比较halfoffset、opset,判定哪些未处理(无结果)消息,并回查业务方本地事务状态。

6.MQ->业务方, 执行checkLocalTransaction方法,查询本地事务状态。返回事务状态给MQ就是步骤4.

需要业务方实现的也就3个方法。

二、消费者

初始化

自定义实现CommandLineRunner接口,执行startConsumer(): spring 容器启动完毕后,执行初始化过程。

1. XXConsumerEntry extends ConsumerEntry。init()子类实现,addConsumerAction()添加具体业务操作。指定一个tag,一个ConsumerExecutor().

2.DefaultMQPushConsumer定义消费者,MessageModel=集群消费,指定消费群组。

(注:这里还可以设置很多参数,例如:consumeMessageBatchMaxSize:一次派发消费多少条(默认1),pullBatchSize:一次拉取多少条(默认32))

3.指定消息监听器:使用base包提供的TracingRocketMQSingleConsumerr。注册监听器TracingRocketMQSingleConsumer.SingleMessageListenerConcurrently。实际上就是封装的RocketMQ的MessageListener接口,定义了consumeMessage()接口,最终会调用步骤1定义的ConsumerAction的execute()。执行消息的消费。

拉取消费

消费者会从MQ长轮询并发拉取消息,并根据初始化的MessageLister接口执行业务消费逻辑。

4.MQ根据返回的状态,如果是RECONSUME_LATER重试,就会入SCHEDULE延迟队列、RETRY重试队列、DLQ死信队列。要注意的是:进入死信队列的消息,需要管理员手动排查问题。

需要业务方实现1个方法

四、 其它细节

4.1.从哪里开始消费

consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

CONSUME_FROM_LAST_OFFSET:一个新的订阅组第一次启动从队列的最后位置开始消费,后续再启动接着上次消费的进度开始消费

CONSUME_FROM_FIRST_OFFSET:一个新的订阅组第一次启动从队列的最前位置开始消费,后续再启动接着上次消费的进度开始消费

CONSUME_FROM_TIMESTAMP:一个新的订阅组第一次启动从指定时间点开始消费,后续再启动接着上次消费的进度开始消费

4.2.一些问题排查思路

理解了RocketMQ原理,数据流转,对排查问题可以提供思路。

1.队列数据膨胀

RMQ_SYS_TRANS_HALF_TOPIC膨胀:可能是死循环了。定时任务反查事务状态,一直消费不完。

RMQ_SYS_TRANS_OP_HALF_TOPIC膨胀:业务量暴增,接口被刷。

RETRY重试、DLQ死信队列膨胀:可能是服务不可用。

2.rocketMQ业务异常日志,具体判断。

3.broker延迟可能reblance失衡。

4.3 唯一消息ID

msgId  transacctionId

MessageExt extends Message :transacctionId是Message字段,msgId是MessageExt的拓展字段。

MessageExt的transactionId就是RocketMQ认为的唯一ID,消息在RocketMQn内部流转,transactionId不变,msgId 会变。看下图就明白了:

下图是生产环境rocketMQ 异常时的日志总结,注意图中newMsgId=msgId   realMsgId=transactionId

注意:这里transacctionId就是RocktMQ认定的唯一事务ID。这里是说对应一个事务,但是不一定适合做接口幂等性(消息重复消费问题)。接口幂等性是与业务耦合的,保证多次执行,同一结果。

幂等性如何实现?

  • 天然幂等性:纯读接口
  • 后天校验型:状态机校验、业务key校验,等等。

基于RocketMQ实现分布式事务(半消息事务)的更多相关文章

  1. 【分布式事务】基于RocketMQ搭建生产级消息集群?

    导读 目前很多互联网公司的系统都在朝着微服务化.分布式化系统的方向在演进,这带来了很多好处,也带来了一些棘手的问题,其中最棘手的莫过于数据一致性问题了.早期我们的软件功能都在一个进程中,数据的一致性可 ...

  2. SpringCloud+RocketMQ实现分布式事务

    随着互联网公司的微服务越来越多,分布式事务已经成为了我们的经常使用的.所以我们来一步一步的实现基于RocketMQ的分布式事务.接下来,我们将要做的主题写出来. RocketMQ的分布式事务结构和说明 ...

  3. 分布式事务之如何基于RocketMQ的事务消息特性实现分布式系统的最终一致性?

    导读 在之前的文章中我们介绍了如何基于RocketMQ搭建生产级消息集群,以及2PC.3PC和TCC等与分布式事务相关的基本概念(没有读过的读者详见

  4. 分布式开放消息系统RocketMQ的原理与实践(消息的顺序问题、重复问题、可靠消息/事务消息)

    备注:1.如果您此前未接触过RocketMQ,请先阅读附录部分,以便了解RocketMQ的整体架构和相关术语2.文中的MQServer与Broker表示同一概念 分布式消息系统作为实现分布式系统可扩展 ...

  5. 分布式事务(3)---RocketMQ实现分布式事务原理

    分布式事务(3)-RocketMQ实现分布式事务原理 之前讲过有关分布式事务2PC.3PC.TCC的理论知识,博客地址: 1.分布式事务(1)---2PC和3PC原理 2.分布式事务(2)---TCC ...

  6. 分布式事务(4)---RocketMQ实现分布式事务项目

    RocketMQ实现分布式事务 有关RocketMQ实现分布式事务前面写了一篇博客 1.RocketMQ实现分布式事务原理 下面就这个项目做个整体简单介绍,并在文字最下方附上项目Github地址. 一 ...

  7. RocketMQ(消息重发、重复消费、事务、消息模式)

    分布式开放消息系统(RocketMQ)的原理与实践 RocketMQ基础:https://github.com/apache/rocketmq/tree/rocketmq-all-4.5.1/docs ...

  8. 谈谈分布式事务之二:基于DTC的分布式事务管理模型[下篇]

    [续上篇] 当基于LTM或者KTM的事务提升到基于DTC的分布式事务后,DTC成为了本机所有事务型资源管理器的管理者:此外,当一个事务型操作超出了本机的范 围,出现了跨机器的调用后,本机的DTC需要于 ...

  9. 【RocketMQ】【分布式事务】使用RocketMQ实现分布式事务

    参考地址:https://blog.csdn.net/zyw23zyw23/article/details/79070044 视频地址:https://v.youku.com/v_show/id_XO ...

  10. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案

    C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...

随机推荐

  1. 使用C#做为游戏开发的服务器语言方案

    Scut开源服务器 开源C#/Python/Lua 手游服务器 主页:http://www.scutgame.com/index.html 开源:https://github.com/ScutGame ...

  2. 从嘉手札<2023-10-16>

    一.商君书 1)更法 商鞅和甘龙.杜挚同秦孝公商量变法. 后两者认为变法会动移已有的社会结构,"圣人不易民而教,知者不变法而治""法古无过,循礼无邪" 但商鞅( ...

  3. TP5图片处理常见问题

    一.Class 'think\Image' not found composer require topthink/think-image 装上了扩展控制器头部加了 use think\Image然后 ...

  4. Jackson objectMapper.readValue 方法 详解

    直接说结论方便一目了然: 1. 简单的直接Bean.class 2. 复杂的用 TypeReference 这样就完事了. public class TestMain2 { public static ...

  5. 当我们在谈 .NET Core 跨平台时,我们在谈些什么?--学习笔记

    摘要 .NET Framework在过去十多年在跨平台上的尝试. .NET Core跨平台的实现有何不同? 基于 .NET Standard的平台兼容性是如何实现的? 讲师介绍 历史枷锁 .NET F ...

  6. 从零开始的微信小程序入门教程(二),初识WXML与WXSS

    壹 ❀ 引 时隔大半年,我终于开始写小程序入门教程的第二篇了,其实我也在纳闷,这么久的时间我到底干了什么,仔细一想,我学了JavaScript部分进阶知识,学了ES6,系统性的去复习了angularj ...

  7. NC200195 区区区间

    题目链接 题目 题目描述 \(Keven\) 特别喜欢线段树,他给你一个长度为 \(n\) 的序列,对序列进行 \(m\) 次操作. 操作有两种: 1 \(1\ l\ r\ k\) :表示将下标在 \ ...

  8. [WPF] MediaElement播放HDR视频泛黄、颜色显示不正确应该如何解决?

    当我们在使用MediaElement控件播放HDR视频时会遇到颜色发灰.泛黄的情况,难道是因为控件做的有问题? 其实并不是程序问题,只是我们普通的应用程序工作在8bit色深的环境中,而HDR色深为10 ...

  9. Java图片加水印

    采用Java自带的Image IO 废话不多说,上菜 1.  文字水印 1 import sun.font.FontDesignMetrics; 2 3 import javax.imageio.Im ...

  10. Java 21 虚拟线程如何限流控制吞吐量

    虚拟线程(Virtual Threads)是 Java 21 所有新特性中最为吸引人的内容,它可以大大来简化和增强Java应用的并发性.但是,随着这些变化而来的是如何最好地管理此吞吐量的问题.本文,就 ...