RocketMQ实现事务消息
在RocketMQ4.3.0版本后,开放了事务消息这一特性,对于分布式事务而言,最常说的还是二阶段提交协议,那么RocketMQ的事务消息又是怎么一回事呢,这里主要带着以下几个问题来探究一下RocketMQ的事务消息:
事务消息是如何实现的
我们有哪些手段来监控事务消息的状态
事务消息的异常恢复机制
RocketMQ的事务消息是如何实现的
RocketMQ作为一款消息中间件,主要作用就是帮助各个系统进行业务解耦,以及对消息流量有削峰填谷的作用,而对于事务消息,主要是通过消息的异步处理,可以保证本地事务和消息发送同时成功执行或失败,从而保证数据的最终一致性,
事务消息从诞生到结束的整个时间线流程:
生产者发送消息到broker,该消息是prepare消息,且事务消息的发送是同步发送的方式。
broker接收到消息后,会将该消息进行转换,所有的事务消息统一写入Half Topic,该Topic默认是RMQ_SYS_TRANS_HALF_TOPIC ,写入成功后会给生产者返回成功状态。
本地生产获取到该消息的事务Id,进行本地事务处理。
本地事务执行成功提交Commit,失败则提交Rollback,超时提交或提交Unknow状态则会触发broker的事务回查。
若提交了Commit或Rollback状态,Broker则会将该消息写入到Op Topic,该Topic默认是RMQ_SYS_TRANS_OP_HALF_TOPIC,该Topic的作用主要记录已经Commit或Rollback的prepare消息,Broker利用Half Topic和Op Topic计算出需要回查的事务消息。如果是commit消息,broker还会将消息从Half取出来存储到真正的Topic里,从而消费者可以正常进行消费,如果是Rollback则不进行其他操作
如果本地事务执行超时或返回了Unknow状态,则broker会进行事务回查。若生产者执行本地事务超过6s则进行第一次事务回查,总共回查15次,后续回查间隔时间是60s,broker在每次回查时会将消息再在Half Topic写一次。回查次数和时间间隔都是可配置的。
执行事务回查时,生产者可以获取到事务Id,检查该事务在本地执行情况,返回状态同第一次执行本地事务一样。
从上述流程可以看到事务消息其实只是保证了生产者发送消息成功与本地执行事务的成功的一致性,消费者在消费事务消息时,broker处理事务消息的消费与普通消息是一样的,若消费不成功,则broker会重复投递该消息16次,若仍然不成功则需要人工介入。
事务消息的成功投递是需要经历三个Topic的
Half Topic:用于记录所有的prepare消息
Op Half Topic:记录已经提交了状态的prepare消息
Real Topic:事务消息真正的Topic,在Commit后会才会将消息写入该Topic,从而进行消息的投递
理解清楚事务消息在这三个Topic的流转就基本理解清楚了RocketMQ的事务消息的处理。接下来我们看看在源码中是如何使用这三个Topic的。
事务消息是如何处理回查的
在RocketMQ中,消息都是顺序写随机读的,以offset来记录消息的存储位置与消费位置,所以对于事务消息的prepare消息来说,不可能做到物理删除,broker启动时每间隔60s会开始检查一下有哪些prepare消息需要回查,从上面的分析我们知道,所有prepare消息都存储在Half Topic中,那么如何从该Topic中取出需要回查的消息进行回查呢?这就需要Op Half Topic以及一个内部的消费进度计算出需要回查的prepare消息进行回查:
Half Topic 默认Topic是RMQ_SYS_TRANS_HALF_TOPIC,建一个队列,存储所有的prepare消息
Op Half Topic默认是RMQ_SYS_TRANS_OP_HALF_TOPIC,建立的对列数与Half Topic相同,存储所有已经确定状态的prepare消息(rollback与commit状态),消息内容是该条消息在Half Topic的Offset
Half Topic消费进度,默认消费者是CID_RMQ_SYS_TRANS,每次取prepare消息判断回查时,从该消费进度开始依次获取消息。
Op Half Topic消费进度,默认消费者是CID_RMQ_SYS_TRANS,每次获取prepare消息都需要判断是否在Op Topic中已存在该消息了,若存在表示该prepare消息已结束流程,不需要再进行事务回查,每次判断都是从Op Topic中获取一定消息数量出来进行对比的,获取的消息就是从Op Topic中该消费进度开始获取的,最大一次获取32条。
获取Half Topic的所有队列,循环队列开始检测需要获取的prepare消息,实际上Half Topic只有一个队列。
获取Half Topic与Op Half Topic的消费进度。
调用fillOpRemoveMap方法,获取Op Half Topic中已完成的prepare事务消息。
从Half Topic中当前消费进度依次获取消息,与第3步获取的已结束的prepare消息进行对比,判断是否进行回查:
如果Op消息中包含该消息,则不进行回查,
如果不包含,获取Half Topic中的该消息,判断写入时间是否符合回查条件,若是新消息则不处理下次处理,并将消息重新写入Half Topic,判断回查次数是否小于15次,写入时间是否小于72h,如果不满足就丢弃消息,若满足则更新回查次数,并将消息重新写入Half Topic并进行事务回查,
在循环完后重新更新Half Topic与Op Half Topic中的消费进度,下次判断回查逻辑时,将从最新的消费进度获取信息。
生产客户端的ClientRemotingProcessor的processRequest方法会处理服务端的CHECK_TRANSACTION_STATE请求,最后会调用checkLocalTransactionState方法,该方法就是业务方可以自己实现事务消息回查逻辑的地方,并将结果最后用endTransactionOneway方法返回给Broker,该执行逻辑可以通过ClientRemotingProcessor的方法processRequest依次理解就可以了。
我们有哪些手段来监控事务消息的状态
事务消息主要有三个状态:
UNKNOW状态:表示事务消息未确定,可能是业务方执行本地事务逻辑时间耗时过长或者网络原因等引起的,该状态会导致broker对事务消息进行回查,默认回查总次数是15次,第一次回查间隔时间是6s,后续每次间隔60s,
ROLLBACK状态,该状态表示该事务消息被回滚,因为本地事务逻辑执行失败导致
COMMIT状态,表示事务消息被提交,会被正确分发给消费者。
那么监控事务消息时,主要是查看该事务消息是否是处于我们想要的状态,而在事务消息生产者发送prepare消息成功后只能拿到一个transactionId,该id不是的RocketMQ消息存储的物理offset地址,RocketMQ只有在准备写入commitlog文件时才会生成真正的msgId,而这里可以获取的transactionId和msgId都是客户端生成的一个消息的唯一标识符,我们在这里称为uniqId,在broker端,会把该uniqId作为一个msgKey写入消息,所以可以通过该uniqId来查找uniqId的一些状态:
通过DefaultMQAdminExt的viewMessage(String topic, String msgId)方法可以消息的信息,这里topic参数是RMQ_SYS_TRANS_HALF_TOPIC ,该topic是真正的Half Topic,msgId传发送prepare消息获取的uniqId,这样可以获取prepare消息在Half Topic真正的offsetMsgId,
通过第一步获取的offsetMsgId继续调用viewMessage(String topic, String msgId)方法,但是topic是RMQ_SYS_TRANS_OP_HALF_TOPIC,这样可以获取Op Half Topic中该事务消息的状态,如果存在说明prepare消息已处理,否则可能仍在回查中或已被丢弃
如果在第二步查到了信息可以用uniqId和事务消息真正Topic继续调用viewMessage(String topic, String msgId)方法获取消息真正的信息,如果存在说明消息已被投递,否则该事务消息已被回滚。只通过Op Half Topic是不能确定消息状态的,这里的sysFlag被设置0,sysFlag是用于确定事务消息状态。
通过上述三步就可以确定事务消息的状态。
事务消息的异常恢复机制
事务消息的异常状态主要有:
生产者提交prepare消息到broker成功,但是当前生产者实例宕机了
生产者提交prepare消息到broker失败,可能是因为提交的broker已宕机
生产者提交prepare消息到broker成功,执行本地事务逻辑成功,但是broker宕机了未确定事务状态
生产提交prepare消息到broker成功,但是在进行事务回查的过程中broker宕机了,未确定事务状态
异常解决:
对于1:事务消息会根据producerGroup搜寻其他的生产者实例进行回查,所以transactionId务必保存在中央存储中,并且事务消息的pid不能跟其他消息的pid混用。
对于2:当前实例会搜寻其他的可用的broker-master进行提交,因为只有提交prepare消息后才会执行本地事务,所以没有影响,注意生产者报的是超时异常时,是不会进行重发的。
对于3:因为返回状态是oneway方式,此时如果消费者未收到消息,需要用手段确定该事务消息的状态,尽快将broker重启,broker重启后会通过回查完成事务消息。
对于4:同3,尽快重启broker。
---------------------
转自:https://blog.csdn.net/qq_28632173/article/details/83790243
RocketMQ实现事务消息的更多相关文章
- RocketMQ支持事务消息机制
事务消费 我们经常支付宝转账余额宝,这是日常生活的一件普通小事,但是我们思考支付宝扣除转账的钱之后,如果系统挂掉怎么办,这时余额宝账户并没有增加相应的金额,数据就会出现不一致状况了. 上述场景在各个类 ...
- 分布式事务之如何基于RocketMQ的事务消息特性实现分布式系统的最终一致性?
导读 在之前的文章中我们介绍了如何基于RocketMQ搭建生产级消息集群,以及2PC.3PC和TCC等与分布式事务相关的基本概念(没有读过的读者详见
- RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)
在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...
- RocketMQ事务消息实现分析
这周RocketMQ发布了4.3.0版本,New Feature中最受关注的一点就是支持了事务消息: 今天花了点时间看了下具体的实现内容,下面是简单的总结. RocketMQ事务消息概要 通过冯嘉发布 ...
- 分布式开放消息系统RocketMQ的原理与实践(消息的顺序问题、重复问题、可靠消息/事务消息)
备注:1.如果您此前未接触过RocketMQ,请先阅读附录部分,以便了解RocketMQ的整体架构和相关术语2.文中的MQServer与Broker表示同一概念 分布式消息系统作为实现分布式系统可扩展 ...
- RocketMQ源码分析之RocketMQ事务消息实现原理中篇----事务消息状态回查
上节已经梳理了RocketMQ发送事务消息的流程(基于二阶段提交),本节将继续深入学习事务状态消息回查,我们知道,第一次提交到消息服务器时消息的主题被替换为RMQ_SYS_TRANS_HALF_TOP ...
- RocketMQ系列(七)事务消息(数据库|最终一致性)
终于到了今天了,终于要讲RocketMQ最牛X的功能了,那就是事务消息.为什么事务消息被吹的比较热呢?近几年微服务大行其道,整个系统被切成了多个服务,每个服务掌管着一个数据库.那么多个数据库之间的数据 ...
- 消息队列之事务消息,RocketMQ 和 Kafka 是如何做的?
每个时代,都不会亏待会学习的人. 大家好,我是 yes. 今天我们来谈一谈消息队列的事务消息,一说起事务相信大家都不陌生,脑海里蹦出来的就是 ACID. 通常我们理解的事务就是为了一些更新操作要么都成 ...
- RocketMQ源码 — 十一、 RocketMQ事务消息
分布式事务是一个复杂的问题,rmq实现了事务的最终一致性,rmq保证本地事务成功消息一定会发送成功并被成功消费,如果本地事务失败了,消息不会被发送. rmq事务消息的实现过程为: producer发送 ...
随机推荐
- Struts2_属性驱动
在jsp页面提交到action中的表单元素在action中是以对象的形式存在的, action中的对象name必须与jsp页面中的表单元素name相同,struts框架自动为action的对象赋值. ...
- Java岗 面试考点精讲(基础篇02期)
1. 两个对象的hashCode相同,则equals也一定为true,对吗? 不对,答案见下面的代码: @Override public int hashCode() { return 1; } 两个 ...
- Java中的强引用和弱引用
旭日Follow_24 的CSDN 博客 ,全文地址请点击: https://blog.csdn.net/xuri24/article/details/81114944 一.强引用 如下是强引用的经典 ...
- 汇编语言--微机CPU的指令系统(五)(标志位操作指令)
(2)标志位操作指令 标志位操作指令是一组对标志位置位.复位.保存和恢复等操作的指令. 1.进位CF操作指令 Ø 清进位指令CLC(Clear Carry Flag):CF←0 Ø 置进位指令STC( ...
- react学习(一)
组件和属性(props) 函数式组件: function Welcome(props) { return <h1>Hello, {props.name}</h1>; } 渲染一 ...
- 使用eclipse初步学习vue.js基础==》v-for的使用 ②
一.步骤演示 1. 新建一个jsp文件 2. 把vue.js放到Web的js目录下 3. 在jsp中引入vue.js <script src="${pageContext.reques ...
- blfs(systemd版本)学习笔记-构建google-chrome浏览器
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一.google-chrome浏览器官网下载地址 我只找到了deb包和rpm包的下载地址 1.https://dl.google ...
- VUE 利用webpack 给生产环境和发布环境配置不同的接口地址
第一步,分别设置不同的接口地址 首先,我们分别找到下面的文件: /config/dev.env.js /config/prod.env.js 其实,这两个文件就是针对生产环境和发布环境设置不同参数的文 ...
- CSS效果:这里有你想要的CSS3漂亮的自定义Checkbox各种复选框
在原来有一篇文章写到了<CSS效果篇--纯CSS+HTML实现checkbox的思路与实例>. 今天这篇文章主要写各种自定义的checkbox复选框,实现如图所示的复选框: 大致的html ...
- 2018-10-19 Chrome插件实现GitHub代码离线翻译v0.0.4
续前文Chrome插件实现GitHub代码翻译v0.0.3. 添加了对驼峰命名的支持. 由于调用浏览器插件-离线英汉词典进行词汇翻译, 因此也不依赖于任何在线翻译服务. Chrome插件: 官网链接 ...