这周RocketMQ发布了4.3.0版本,New Feature中最受关注的一点就是支持了事务消息:

今天花了点时间看了下具体的实现内容,下面是简单的总结。

RocketMQ事务消息概要

通过冯嘉发布的《RocketMQ 4.3正式发布,支持分布式事务》一文可以看到RocketMQ采用了2PC的方案来提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息。

这张图说明了事务消息的大致方案,分为两个逻辑:正常事务消息的发送及提交、事务消息的补偿流程

事务消息发送及提交:

  1. 发送消息(half消息)
  2. 服务端响应消息写入结果
  3. 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)
  4. 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)

补偿流程:

  1. 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”
  2. Producer收到回查消息,检查回查消息对应的本地事务的状态
  3. 根据本地事务状态,重新Commit或者Rollback

补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况。

以上RocketMQ事务消息的整体方案,对于了解Notify的同学应该是很熟悉的,下面是之前Notify相关的资料:

整体方案是完全相同的,只是两者的Storage不同。

RocketMQ事务消息设计

一阶段的消息如何对用户不可见

事务消息相对普通消息最大的特点就是一阶段发送的消息对用户是不可见的。

如何做到写入了消息但是对用户不可见?——写入消息数据,但是不创建对应的消息的索引信息。

熟悉RocketMQ的同学应该都清楚,消息在服务端的存储结构如上,每条消息都会有对应的索引信息,Consumer通过索引读取消息。

那么实现一阶段写入的消息不被用户消费(需要在Commit后才能消费),只需要写入Storage Queue,但是不构建Index Queue即可。

RocketMQ中具体实现策略是:写入的如果事务消息,对消息的Topic和Queue等属性进行替换,同时将原来的Topic和Queue信息存储到消息的属性中。

上图即RocketMQ替换事务消息属性的代码实现,替换属性后这条消息被写入到TransactionalMessageUtil.buildHalfTopic()的Queue 0中。

(RocketMQ将事务消息一阶段发送的消息称为Half消息让人费解,采用的2PC的方式,一阶段消息称为Prepare Message或者Pending Message更能体现它的含义)

在完成Storage Queue的写入后,在appendCallback中,普通消息会去构建消息索引,而如果发现是事务消息,则跳过了创建索引的逻辑。

如果让一阶段的消息对用户可见

在完成一阶段写入一条对用户不可见的消息后,二阶段如果是Commit操作,则需要让消息对用户可见;如果是Rollback则需要撤销一阶段的消息。

先说Rollback的情况。对于Rollback,本身一阶段的消息对用户是不可见的,其实不需要真正撤销消息(实际上RocketMQ也无法去真正的删除一条消息,因为是顺序写文件的)。

但是区别于这条消息没有确定状态(Pending状态,事务悬而未决),需要一个操作来标识这条消息的最终状态。

RocketMQ事务消息方案中引入了Op消息的概念,用Op消息标识事务消息是否状态已经确定(Commit或者Rollback)。如果一条事务消息没有对应的Op消息,说明这个事务的状态还无法确定(可能是二阶段失败了)。

引入Op消息后,事务消息无论是Commit或者Rollback都会记录一个Op操作。

Commit相对于Rollback只是在写入Op消息前创建Half消息的索引。

Op消息的存储

RocketMQ将Op消息写入到全局一个特定的Topic中:TransactionalMessageUtil.buildOpTopic()

这个Topic是一个内部的Topic(像Half消息的Topic一样),不会被用户消费。

Op消息的内容为对应的Half消息的存储的Offset,这样通过Op消息能索引到Half消息进行后续的回查操作。

Half消息的索引构建

在执行二阶段的Commit操作时,需要构建出Half消息的索引。

一阶段的Half消息由于是写到一个特殊的Topic,所以二阶段构建索引时需要读取出Half消息,并将Topic和Queue替换成真正的目标的Topic和Queue,之后通过一次普通消息的写入操作来生成一条对用户可见的消息。

所以RocketMQ事务消息二阶段其实是利用了一阶段存储的消息的内容,在二阶段时恢复出一条完整的普通消息,然后走一遍消息写入流程。

如何处理二阶段失败的消息

如果二阶段失败了,比如在Commit操作时出现网络问题导致Commit失败,那么需要通过一定的策略使这条消息最终被Commit。

RocketMQ采用了一种补偿机制,称为“回查”。

Broker端对未确定状态的消息发起回查,将消息发送到对应的Producer端(同一个Group的Producer),由Producer根据消息来检查本地事务的状态,进而执行Commit或者Rollback。

Broker端通过对比Half消息和Op消息进行事务消息的回查并且推进CheckPoint(记录那些事务消息的状态是确定的)。

值得注意的一点是具体实现中,在回查前,系统会执行putBackHalfMsgQueue操作,即将Half消息重新写一遍到Half消息的Queue中。这么做其实是为了能有效的推进上面的CheckPoint。

RocketMQ事务消息设计总结

以上是RocketMQ事务消息实现的示意图:

  • 通过写Half消息的方式来实现一阶段消息对用户不可见
  • 通过Op消息来标记事务消息的状态
  • 通过读取Half消息来生成一条新的Normal消息来完成二阶段Commit之后消息对Consumer可见
  • 通过Op消息来执行回查

优势:

  • Half Queue和Op Queue的数量可控,不会随着Topic的增加而增加
  • 没有外部依赖,实现自包含

缺陷:

  • 每条事务消息至少需要写一条Half消息(异常情况可能会有多条)和Normal,写放大了
  • 所有Half消息都是写到全局预设的一个内部的Topic,这块可能性能会有一些问题(所有Topic的事务消息会往一个Topic上写)
  • 全局Op消息写一个Topic,回查时间可能会有相互影响

RocketMQ事务消息实现分析的更多相关文章

  1. RocketMQ 事务消息示例分析

    @ 目录 1 示例模式 2 安装与配置 RocketMQ 3 运行服务 3.1 启动 NameServer 3.2 启动 broker 4 生产者 4.1 事务监听器 4.2 事务消息生产者 5 消费 ...

  2. RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)

    在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...

  3. RocketMQ源码分析之从官方示例窥探:RocketMQ事务消息实现基本思想

    摘要: RocketMQ源码分析之从官方示例窥探RocketMQ事务消息实现基本思想. 在阅读本文前,若您对RocketMQ技术感兴趣,请加入RocketMQ技术交流群 RocketMQ4.3.0版本 ...

  4. rocketmq事务消息

    rocketmq事务消息 参考: https://blog.csdn.net/u011686226/article/details/78106215 https://yq.aliyun.com/art ...

  5. 搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务

    搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 初步认识RocketMQ的核心模块 rocketmq模块 rocketmq-broker:接受生产者发来的消息并存储(通过调用rocke ...

  6. rocketmq事务消息入门介绍

    说明 周五的时候发了篇:Rocketmq4.3支持事务啦!!!,趁着周末的时候把相关内容看了下,下面的主要内容就是关于RocketMQ事务相关内容介绍了. 说明: 今天这篇仅仅是入门介绍,并没有涉及到 ...

  7. RocketMQ事务消息学习及刨坑过程

    一.背景 MQ组件是系统架构里必不可少的一门利器,设计层面可以降低系统耦合度,高并发场景又可以起到削峰填谷的作用,从单体应用到集群部署方案,再到现在的微服务架构,MQ凭借其优秀的性能和高可靠性,得到了 ...

  8. 关于 RocketMQ 事务消息的正确打开方式 → 你学废了吗

    开心一刻 昨晚和一哥们一起吃夜宵,点了几瓶啤酒 不一会天空下起了小雨,哥们突然道:糟了 我:怎么了 哥们:外面下雨了,我老婆还在等着我去接她 他给了自己一巴掌,说道:真他妈不是个东西 我心想:哥们真是 ...

  9. RocketMQ 事务消息

    RocketMQ 事务消息在实现上充分利用了 RocketMQ 本身机制,在实现零依赖的基础上,同样实现了高性能.可扩展.全异步等一系列特性. 在具体实现上,RocketMQ 通过使用 Half To ...

随机推荐

  1. System.Web.Caching.Cache缓存帮助类

    /// <summary> /// 缓存帮助类 /// </summary> public class CacheHelper { /// <summary> // ...

  2. Scrum冲刺阶段6

    成员今日完成的任务 人员 任务 何承华 学习后端设计 陈宇 后端设计 丁培辉 学习后端设计 温志铭 信息界面设计 杨宇潇 信息界面界面设计 张主强 服务器构建学习 成员遇到的问题 人员 问题 何承华 ...

  3. python第三天基础之字符编码

    一 了解字符编码的知识储备 1. 文本编辑器存取文件的原理(nodepad++,pycharm,word) 打开编辑器就打开了启动了一个进程,是在内存中的,所以在编辑器编写的内容也都是存放与内存中的, ...

  4. js unicode转中文 &#26041;&#26696;&#27010;&#36848;&#32852;&#32593;LED&#29031;&#26126;&#26041;&#26696;&#21487;&#25191;&#34892;&#20840;&#37096;&#30340;DALI &#21644;

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Book : <Hands-on ML with Sklearn & TF> pdf/epub

    非常好的书,最近发现了pdf版本,链接:http://www.finelybook.com/hands-on-machine-learning-with-scikit-learn-and-tensor ...

  6. LinkeList 特有方法

    LinkedList:特有方法:addFirst();addLast();添加元素到集合,添加到头尾,getFirst();getLast();获取元素,但不删除元素.如果集合中没有元素,会出现NoS ...

  7. Exp2 后门原理与实践 20154320 李超

    目录- 基础问题回答- 基础知识- 实验过程- 实验心得体会 基础知识问答 1. 例举你能想到的一个后门进入到你系统中的可能方式?从不安全的网站上下载的程序可能存在后门. 2. 例举你知道的后门如何启 ...

  8. Android 极光推送造成IM服务绑定失败bug

    由于极光推送对8.0的支持问题,升级到了最新版本的极光推送.但是最新版本的极光推送,默认将推送服务设置到了新的进程里面,由此引发 Android 极光推送多进程造成的application运行两次 和 ...

  9. android 图片内存管理

    图片对象: drawable bitmap etc.图片对象在Android上该缓存吗?什么时候缓存?怎么缓存?缓存后使用时怎么取出?怎么销毁?什么时候销毁? bitmap对象(new出来的) :需要 ...

  10. android 图形图像

    Canvas 画布 paint 画笔 Path 路径Path代表任意多条直线连接而成的任意图形,当Canvas根据Path绘制时,它可以绘制出任意的形状 使用 Matrix 控制图像或组件变换步骤:① ...