一站式了解RocketMQ如何实现顺序消息😵

引言

每种MQ对顺序消息的实现都各有差异,我们今天主要来讲讲RocketMQ是如何实现顺序消息的。请注意,由于全局顺序消息不常用,我们这里提到的顺序消息都是指分区顺序消息。

顺序消息的定义😣

RocketMQ中的顺序消息分为全局顺序消息和分区顺序消息。

全局顺序消息

全局顺序消息是指某个Topic下的所有消息都能够按照发送的顺序被消费。这里稍微解释一下,为什么全局顺序消息不常用,因为全局顺序消息通常意味着在一个 Topic 下只有一个队列。如果要求整个 Topic 的所有消息都严格有序,那么必须确保所有的消息都发送到同一个队列,并且由同一个消费者实例按序消费。这就意味着,在这种情况下,该 Topic 只能配置一个队列。

然而,这也意味着性能受限于单个队列和单个消费者的处理能力,因为不能利用多队列带来的并行处理能力。因此,全局顺序消息适用于对顺序要求极高、但吞吐量需求不高的场景

所以大多数应用场景,更常见的是使用分区顺序消息,即保证某些类型的消息有序即可。

分区顺序消息

分区顺序消息则是指在消息被划分到不同的队列中时,每个队列内部的消息能够保证顺序消费。实际应用中,分区顺序消息更为常用,因为它能在保证一定顺序的同时提供更好的并发性能

为了保证消息的顺序性,对于需要保证顺序的消息,生产者发送消息时必须确保相同业务逻辑的消息发送到同一个队列 中。这通常是通过消息的Key进行哈希取模 来决定消息发送到哪个队列。同时,消费者也需要采用单线程模式消费该队列中的消息,避免多线程环境下由于竞争导致的消息顺序错乱。

生产者端🤓

RocketMQ从生产者端和消费者端两个方面来保证了分区顺序消息机制,我们也从这两个方面来讲。

RocketMQ在生产者端通过MessageQueueSelector来保证顺序消息,传入orderId,id % mqs.size()保证消息会传入同一个队列,代表属于这个订单id(具体业务)的消息都传入这个队列,这里就可以保证队列局部有序

MessageQueueSelector 接口介绍

  • mqs: 当前 Topic 对应的所有队列列表。
  • msg: 当前要发送的消息对象。
  • arg: 用户自定义参数,可以在发送消息时传入。
  • 返回值:选择出的一个 MessageQueue,即这条消息将被发送到该队列。
java 复制代码
public interface MessageQueueSelector {
    MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}

使用MessageQueueSelector

下面是一个典型的使用 MessageQueueSelector 来保证订单消息顺序性的例子:

java 复制代码
// 发送消息时指定 selector
String orderId = "ORDER_20250623_001";
producer.send(msg, new MessageQueueSelector() {
    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        Integer id = (Integer) arg;
        int index = id % mqs.size();
        return mqs.get(index);
    }
}, orderId.hashCode());

在这个例子中,所有具有相同 orderId 的消息都会被发送到同一个队列中,从而保证了这些消息在消费端是有序的。

消费者端😎

光有生产者用 MessageQueueSelector 把消息发到固定队列还不够,还需要消费者端配合才能保证顺序消费:

  • 消费者必须采用 单线程消费模式 来消费每个队列的消息。
  • RocketMQ 默认使用并发消费(多线程),所以需要设置 consumeThreadMinconsumeThreadMax 为 1,或者使用 MessageListenerOrderly 来监听消息。

ps:MessageListenerOrderly 保证的是:同一个队列中的消息,在一个消费者组内,只被同一个消费者实例串行消费

它确保每个队列的消息只能被该消费组中的一个消费者实例消费,并且是串行消费(即一条一条地处理,不并发),这样可以保证同一个队列中消息的顺序性。

那么同一个消费者在处理完这个一系列业务消息之前,不能处理其他队列的消息吗?虽然 MessageListenerOrderly 确保了单个 MessageQueue 内的消息是串行处理的,但它并不限制整个消费者实例同时处理多个不同的 MessageQueue。也就是说,不同 MessageQueue 上的消息仍然可以并行处理,只是在同一时间点上,每个 MessageQueue 只能由一个线程进行消费

示例:

java 复制代码
consumer.registerMessageListener(new MessageListenerOrderly() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, MessageListenerOrderly.ConcurrentlyTopicSubSet context) {
        for (MessageExt msg : msgs) {
            System.out.println("消费消息: " + new String(msg.getBody()));
        }
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});

总结❤️

这就是RocketMQ实现顺序消息的解析

如果你看了这篇文章有收获可以点赞+关注+收藏🤩,这是对笔者更新的最大鼓励!如果你有更多方案或者文章中有错漏之处,请在评论区提出帮助笔者勘误,祝你拿到更好的offer!

相关推荐
掘金码甲哥1 小时前
Golang 文本模板,你指定没用过!
后端
lwb_01181 小时前
【springcloud】快速搭建一套分布式服务springcloudalibaba(四)
后端·spring·spring cloud
张先shen3 小时前
Spring Boot集成Redis:从配置到实战的完整指南
spring boot·redis·后端
Dolphin_海豚3 小时前
一文理清 node.js 模块查找策略
javascript·后端·前端工程化
EyeDropLyq4 小时前
线上事故处理记录
后端·架构
MarkGosling6 小时前
【开源项目】网络诊断告别命令行!NetSonar:开源多协议网络诊断利器
运维·后端·自动化运维
Codebee6 小时前
OneCode3.0 VFS分布式文件管理API速查手册
后端·架构·开源
_新一6 小时前
Go 调度器(二):一个线程的执行流程
后端
estarlee6 小时前
腾讯云轻量服务器创建镜像免费API接口教程
后端
风流 少年7 小时前
Cursor创建Spring Boot项目
java·spring boot·后端