一文搞定RocketMQ面试


1. 消息顺序性问题

通过队列确保顺序性

  • 单个消息队列顺序消费:

RocketMQ 支持创建多个主题(Topic),每个主题可以有多个消息队列(Queue)。在单个消息队列的情况下,RocketMQ 能够保证消息的顺序性。这意味着,当一组消息发送到同一个队列时,它们将按照发送的顺序被消费者接收。

  • 有序消息生产者:

在发送消息时,RocketMQ 提供了一个参数来指定消息的顺序,即通过设置消息的 key 来确保消息被发送到同一个队列。这样,相同 key 的消息会被发送到同一个队列,进而保证消费的顺序性。

  • 有序消息消费者:

对于顺序消费,RocketMQ 提供了 MessageListenerOrderly 接口,用于注册顺序消费者。当消息到达时,RocketMQ 会确保同一个消费者组的消息按顺序传递给该消费者。这样,消费者可以保证按照发送顺序处理消息。

  • 顺序消息发送与消费的分区顺序:

RocketMQ 提供了基于分区的顺序消息发送与消费。通过将消息发送到特定的分区或者在消费时指定分区,可以保证消息的顺序性。这样,所有被发送到同一个分区的消息都会按顺序发送和消费。

  • 顺序消息轨迹追踪:

RocketMQ 提供了消息轨迹(Message Trace)功能,可以追踪消息的发送、存储和消费过程。通过查看消息轨迹,可以更好地理解消息的处理过程,并排查消息顺序性问题。

2. 消息重复性问题

RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重

在网络不稳定的情况下,RocketMQ会出现消息重复的问题:

  • 发送时消息重复

当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

  • 投递时消息重复

消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

  • 负载均衡时消息重复

当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时,会触发 Rebalance,此时消费者可能会收到重复消息。

造成消息重复的根本原因是:网络不可达。只要通过网络交换数据,就无法避免这个问题。
所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

  • 消费端处理消息的业务逻辑保持幂等性
  • 保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现
第1条很好理解,只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样。

第2条原理就是利用一张日志表来记录已经处理成功的消息的ID,如果新到的消息ID已经在日志表中,那么就不再处理这条消息。

第1条解决方案,很明显应该在消费端实现,不属于消息系统要实现的功能。

第2条可以消息系统实现,也可以业务端实现。正常情况下出现重复消息的概率其实很小,如果由消息系统来实现的话,肯定会对消息系统的吞吐量和高可用有影响,所以最好还是由业务端自己处理消息重复的问题,这也是RocketMQ不解决消息重复的问题的原因。

3. 事务消息

通过消息队列, 事务消息能达到分布式事务的最终一致

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

事务消息执行流程:

    1. 发送方向消息队列 RocketMQ 服务端发送消息。
    1. 服务端将消息持久化成功之后,向发送方 ACK 确认消息已经发送成功,此时消息为半消息。
    1. 发送方开始执行本地事务逻辑。
    1. 发送方根据本地事务执行结果向服务端提交二次确认(Commit 或是 Rollback),服务端收到 Commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;服务端收到 Rollback 状态则删除半消息,订阅方将不会接受该消息。
    1. 在断网或者是应用重启的特殊情况下,上述步骤 4 提交的二次确认最终未到达服务端,经过固定时- 间后服务端将对该消息发起消息回查。
    1. 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
    1. 发送方根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤 4 对半消息进行操作。

4. 消息如何发送

有三种:可靠同步发送、可靠异步发送、单向发送

4.1. 可靠同步发送

同步发送是指消息发送方发出数据后,会在收到接收方发回响应之后才发下一个数据包的通讯方式。

4.2. 可靠异步发送

异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。 MQ 的异步发送,需要用户实现异步发送回调接口(SendCallback)。消息发送方在发送了一条消息后,不需要等待服务器响应即可返回,进行第二条消息发送。发送方通过回调接口接收服务器响应,并对响应结果进行处理。

应用场景:异步发送一般用于链路耗时较长,对 RT 响应时间较为敏感的业务场景,例如用户视频上传后通知启动转码服务,转码完成后通知推送转码结果等。

4.3. 单向发送

单向(Oneway)发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。 此方式发送消息的过程耗时非常短,一般在微秒级别。

应用场景:适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。 下表概括了三者的特点和主要区别。

三种方式比较

5. 消息订阅模式

有集群订阅和广播订阅

5.1 集群订阅

同一个 Group ID 所标识的所有 Consumer 平均分摊消费消息。 例如某个 Topic 有 9 条消息,一个 Group ID 有 3 个 Consumer 实例,那么在集群消费模式下每个实例平均分摊,只消费其中的 3 条消息。

5.2 广播订阅

同一个 Group ID 所标识的所有 Consumer 都会各自消费某条消息一次。 例如某个 Topic 有 9 条消息,一个 Group ID 有 3 个 Consumer 实例,那么在广播消费模式下每个实例都会各自消费 9 条消息。

6. 定时消息和延时消息

  • 定时消息:Producer 将消息发送到消息队列 RocketMQ 服务端,但并不期望这条消息立马投递,而是推迟到在当前时间点之后的某一个时间投递到 Consumer 进行消费,该消息即定时消息。

  • 延时消息:Producer 将消息发送到消息队列 RocketMQ 服务端,但并不期望这条消息立马投递,而是延迟一定时间后才投递到 Consumer 进行消费,该消息即延时消息。

7. 集群

7.1 单master模式

在单Master模式下,整个 RocketMQ 集群只有一个 Master 节点,没有 Slave 节点。这种模式下,Master 负责消息的写入和读取,但是不具备高可用性。

7.2 多master模式

多Master模式下,RocketMQ 集群包含多个 Master 节点,每个 Master 节点都有独立的存储和负载。Producer 可以选择发送消息到任何一个 Master 节点,而 Consumer 则可以从任何一个 Master 节点消费消息

7.3 一主多从模式

主从模式下,RocketMQ 集群由多个 Master 节点和相应数量的 Slave 节点组成。Master 负责消息的写入和读取,而 Slave 则作为 Master 的备份。Slave 节点会同步 Master 节点上的数据,以保证数据的可靠性和高可用性。

7.4 多主多从模式

多主多从模式是主从模式的扩展,它允许在一个 RocketMQ 集群中同时存在多个 Master 节点和多个 Slave 节点。这种模式下,每个 Master 节点都会有相应数量的 Slave 节点进行数据备份和同步

7.5 数据同步方式一:异步复制

优点

  • 即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,因为 Master 宕机后,消费者仍然可以从 Slave 消费,此过程对应用透明。不需要人工干预。性能同多 Master 模式几乎一样。

缺点

  • Master 宕机,磁盘损坏情况,会丢失少量消息。

7.6 数据同步方式二:同步双写

优点

  • 数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高

缺点

  • 性能比异步复制模式略低,大约低 10%左右,发送单个消息的 RT 会略高。目前主宕机后,备机不能自动切换为主机,后续会支持自动切换功能。

8. 消息存储

RocketMQ 的消息存储是指将消息持久化到磁盘中以确保消息的可靠性和持久性。RocketMQ 使用了提交日志(Commit Log)和消息队列文件(Message Queue Files)两种方式来实现消息的存储。

  • 提交日志(Commit Log):

提交日志是 RocketMQ 存储消息的主要方式之一。每个 Broker 都有一个提交日志文件,用于记录所有发送到该 Broker 的消息。当 Producer 发送消息时,消息首先被写入提交日志中,并分配一个全局唯一的消息偏移量(Offset)。提交日志中的消息按照发送顺序进行存储,保证了消息的顺序性。

  • 消息队列文件(Message Queue Files):

消息队列文件存储了按照主题(Topic)和消息队列(Queue)划分的消息。每个主题下的每个消息队列对应一个消息队列文件。当消息从提交日志中被读取并存储到消息队列文件中时,RocketMQ 会将消息按照主题和队列进行分类,并将消息存储到相应的消息队列文件中。消息队列文件中的消息按照消息在提交日志中的偏移量进行存储,以便快速定位和检索消息。

9. 消息堆积

消息堆积是指消息在 RocketMQ 中积累到一定数量或者一段时间内未能及时消费的情况。消息堆积可能会导致消费者处理延迟增加、系统负载过高等问题。以下是处理 RocketMQ 消息堆积的一些常见方法:

  • 增加消费者:

最直接的方法是增加消费者,以加快消息的消费速度。如果消息堆积是由于消费者数量不足导致的,可以通过增加消费者的方式来提高消息的处理速度。

  • 优化消费端处理逻辑:

检查消费端的处理逻辑,确保消费者能够高效地处理消息。优化消费端的代码结构和算法,减少不必要的计算和IO操作,以提高消息的消费效率。

  • 调整消息消费的并发度:

可以通过调整消费者的并发度来增加消息的处理能力。通过增加消费者线程数或者调整消费者组的数量来提高消息的并行处理能力。

  • 监控消息队列状态:

使用 RocketMQ 提供的监控工具或者第三方监控系统,实时监控消息队列的状态和堆积情况。及时发现消息堆积问题,并采取相应的措施进行处理。

  • 手动重平衡消费者:

如果消息堆积是由于某些消费者故障或者宕机导致的,可以手动触发消费者重平衡操作,将消息重新分配给可用的消费者,以确保消息能够及时被消费。

  • 消息重试和死信队列:

对于消费失败的消息,可以设置消息重试策略,让 RocketMQ 自动重新投递消息。如果消息多次消费失败,可以将消息发送到死信队列进行处理,避免消息一直堆积在原始队列中。

  • 调整消息生产速率:

如果消息生产速率过快导致消息堆积,可以调整消息生产者的发送速率,减缓消息的产生速度,以使消费者有足够的时间来处理消息。

10. 消息过滤

Consumer 可以根据消息标签(Tag)对消息进行过滤,确保 Consumer 最终只接收被过滤后的消息类型。消息过滤在消息队列 RocketMQ 的服务端完成。

RocketMQ 实现消息过滤主要有两种方式:Tag 过滤和 SQL 过滤。

  • Tag 过滤

Tag 过滤是最简单的消息过滤方式,通过设置消息的标签来进行过滤。在发送消息时,Producer 可以为每条消息设置一个或多个标签,表示消息的类型或属性。在消费消息时,消费者可以通过指定标签来选择需要消费的消息。只有符合指定标签的消息才会被消费,其他消息则被忽略。这种方式适用于简单的消息过滤场景,但对消息的灵活性和复杂性有一定限制。

  • SQL 过滤

SQL 过滤是一种更加灵活和强大的消息过滤方式,它允许消费者使用 SQL 表达式来定义消息过滤条件。在消费者订阅消息时,可以通过设置 SQL 表达式来筛选满足条件的消息。RocketMQ 使用 Apache RocketMQ Filter 来实现 SQL 过滤功能,支持类似 SQL 的 WHERE 子句语法,可以对消息的属性进行条件过滤。消费者只会消费满足指定条件的消息,其他消息则被过滤掉。这种方式适用于复杂的消息过滤场景,可以根据业务需要灵活定义过滤条件。

11. 参考

[1]RocketMQ特性及面试(上)

[2]RocketMQ特性及面试(下)

[3]rocketmq官方文档



文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录