深入理解RocketMQ消息查询机制
在实际开发中,经常需要查看MQ中消息的内容,RocketMQ提供了多种消息查询方式,给开发和运维带来了极大的便利,一些其他消息中间件,如Kafka,并不具备消息查询能力。
本文对RocketMQ提供到的查询机制和背后原理进行深入的介绍。文章主要包括3个部分:
-
消息查询介绍
:介绍消息查询中使用到的Message Key 、Unique Key、Message Id 的区别 -
消息查询工具
:分别介绍命令行工具、管理平台、客户端API这三种工具的详细用法,以及如何让消费者重新消费特定的消息。 -
实现原理
:介绍Message Key & Unique Key与Message Id的实现机制上区别,Unique Key在精确一次消费(Exactly Once)语义下的作用,以及为什么Message Id查询效率更高。
消息查询介绍
RocketMQ提供了3种消息查询方式:
-
按照Message Key 查询
:消息的key是业务开发同学在发送消息之前自行指定的,通常会把具有业务含义,区分度高的字段作为消息的key,如用户id,订单id等。 -
按照Unique Key查询
:除了业务开发同学明确的指定消息中的key,RocketMQ生产者客户端在发送发送消息之前,会自动生成一个UNIQ_KEY,设置到消息的属性中,从逻辑上唯一代表一条消息。 -
按照Message Id 查询
:Message Id 是消息发送后,在Broker端生成的,其包含了Broker的地址,和在CommitLog中的偏移信息,并会将Message Id作为发送结果的一部分进行返回。Message Id中属于精确匹配,从物理上唯一代表一条消息,查询效率更高。
RocketMQ有意弱化Unique Key与Message Id的区别,有时都称之为Message Id。在通过RocketMQ的命令行工具或管理平台进行查询时,二者可以通用。在根据Unique Key进行查询时,本身是有可能查询到多条消息的,但是查询工具会进行过滤,只会返回一条消息。种种情况导致很多RocketMQ的用户,并未能很好对二者进行区分。
业务开发同学在使用RocketMQ时,应该养成良好的习惯,在发送/消费消息时,将这些信息记录下来,通常是记录到日志文件中,以便在出现问题时进行排查。
以生产者在发送消息为例,通常由以下3步组成:
第1步:构建消息
构建消息对象Message,在这里我们通过setKeys方法设置消息的key,如果有多个key可以使用空格" "进行分割
第2步:发送消息
发送消息,会返回一个SendResult对象表示消息发送结果。
第3步:打印发送结果
结果中包含Unique Key和Message Id,如下所示:
其中:
-
sendStatus
:表示消息发送结果的状态 -
msgId
:注意这里的命名虽然是msgId,但实际上其是Unique Key -
offsetMsgId
:Broker返回的Message ID 。在后文中,未进行特殊说明的情况下,Message ID总是表示offsetMsgId。 -
messageQueue
:消息发送到了哪个的队列,如上图显示发送到broker-a的第0个的队列 -
queueOffset
:消息在队列中的偏移量,每次发送到一个队列时,offset+1
事实上,用户主动设置的Key以及客户端自动生成的Unique Key,最终都会设置到Message对象的properties属性中,如下图所示:
其中:
-
KEYS
:表示用户通过setKeys方法设置的消息key, -
UNIQ_KEY
:表示消息发送之前由RocketMQ客户端自动生成的Unique Key
。细心的读者发现了其值与上述打印SendResult结果中的msgId字段的值是一样的,这验证了前面所说的msgId表示的实际上就是Unique Key的说法。
在了解如何主动设置Key,以及如何获取RocketMQ自动生成的Unique Key和Message Id后,就可以利用一些工具来进行查询。
消息查询工具
RocketMQ提供了3种方式来根据Message Key、Unique Key、Message Id来查询消息,包括:
-
命令行工具
:主要是运维同学使用 -
管理平台
:运维和开发同学都可以使用 -
客户端API
:主要是开发同学使用
命令行工具
RocketMQ自带的mqadmin命令行工具提供了一些子命令,用于查询消息,如下:
管理平台
RocketMQ提供的命令行工具,虽然功能强大,一般是运维同学使用较多。通过RocketMQ提供的管理平台进来行消息查询,则对业务开发同学更加友好。在管理平台的消息一栏,有3个TAB,分别用于:根据Topic时间范围查询、Message Key查询、Message Id查询
客户端API
除了通过命令行工具和管理平台,还可以通过客户端API的方式来进行查询,这其实是最本质的方式,命令行工具和管理平台的查询功能都是基于此实现。
实现原理
Unqiue Key & Message Key都需要利用RocketMQ的哈希索引机制来完成消息查询,由于建立索引有一定的开销,因此Broker端提供了相关配置项来控制是否开启索引。
Message Id是在Broker端生成的,其包含了Broker地址和commit Log offset信息,可以精确匹配一条消息,查询消息更好
。下面分别介绍 Unqiue Key & Message Id的生成和作用。
Unique Key生成
Unique Key是生产者发送消息之前,由RocketMQ 客户端自动生成的,具体来说,RocketMQ发送消息之前,最终都要通过以下方法:
DefaultMQProducerImpl#sendKernelImpl
如上所示,如果不是批量消息,会通过MessageClientIDSetter的setUniqID方法为消息设置Unique key,该方法实现如下所示:
MessageClientIDSetter#setUniqID
如果消息的Unique Key属性为null,就通过createUniqID()方法为消息创建一个新的Unique Key,并设置到消息属性中。之所以要判断Unique Key是否为null与其作用有关。
Unique Key作用
了解Unique Key的作用对于我们理解消息重复的原因有很大的帮助。RocketMQ并不保证消息投递过程中的Exactly Once语义,即消息只会被精确消费一次,需要消费者自己做幂等。而通常导致消息重复消费的原因,主要包括:
-
生产者发送时消息重复:RocketMQ对于无序消息发送失败,默认会重试2次
-
消费者Rebalance时消息重复
导致生产者发送重复消息的原因可能是:一条消息已被成功发送到服务端并完成持久化,由于网络超时此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败,此时生产者将再次尝试发送消息。
在重试发送时,sendKernelImpl会被重复调用,意味着setUniqID方法会被重复调用,不过由于setUniqID方法实现中进行判空处理,因此重复设置Unique Key。在这种情况下,消费者后续会收到两条内容相同并且 Unique Key 也相同的消息(offsetMsgId不同,因为对Broker来说存储了多次)。
那么消费者如何判断,消费重复是因为重复发送还是Rebalance导致的重复消费呢?
消费者实现MessageListener接口监听到的消息类型是MessageExt,可以将其强制转换为MessageClientExt,之后调用getMsgId方法获取Unique Key,调用getOffsetMsgId获得Message Id。如果多消息的Unique Key相同,但是offsetMsgId不同,则有可能是因为重复发送导致。
批量发送模式下的Unique Key
DefaultMQProducer提供了批量发送消息的接口:
在内部,这批消息首先会被构建成一个MessageBatch对象。在前面sendKernelImpl方法中我们也看到了,对于MessageBatch对象,并不会设置Unique Key。这是因为在将批量消息转换成MessageBatch时,已经设置过了。
可能有一部分同学会误以为一个批量消息中每条消息Unique Key是相同的,其实不然,每条消息Unique Key都不同
。
可以参考DefaultMQProducer#batch
方法源码:
Message Id生成
SendResult中的offsetMsgId,即常规意义上我们所说的Message Id是在Broker端生成的,用于唯一标识一条消息,在根据Message Id查询的情况下,最多只能查询到一条消息。Message Id总共 16 字节,包含消息存储主机地址,消息 Commit Log offset。如下图所示:
RocketMQ内部通过一个MessageId对象进行表示:
并提供了一个MessageDecoder对象来创建或者解码MessageId。
Broker端在顺序存储消息时,首先会通过createMessageId方法创建msgId。源码如下所示:
CommitLog.DefaultAppendMessageCallback#doAppend
而客户端在根据msgId向Broker查询消息时,首先会将通过MessageDecoder的decodeMessageId方法,之后直接向这个broker进行查询指定位置的消息。
参见:MQAdminImpl#viewMessage
由于根据Message Id进行查询,实际上是直接从特定Broker的CommitLog中的指定位置进行查询的,属于精确匹配,并不像用户设置的key,或者Unique Key那么样,需要使用到哈希索引机制,因此效率很高。
总结
-
RocketMQ提供了3种消息查询方式:Message Key & Unique Key & Message Id
-
RocketMQ提供了3种消息查询工具:命令行、管理平台、客户端API,且支持将查询到让特定/所有消费者组重新消费
-
RocketMQ有意对用户屏蔽Unique Key & Message Id区别,很多地方二者可以通用
-
Message Key & Unique Key 需要使用到哈希索引机制,有额外的索引维护成本
-
Message Id由Broker和commit log offset组成,属于精确匹配,查询效率更好
__EOF__

本文链接:https://www.cnblogs.com/mpyidudu/p/15887628.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix