当前位置: 首页 > article >正文

MQ保证消息的顺序性

在消息队列(MQ)中保证消息的顺序性是一个常见的需求,尤其是在需要严格按顺序处理业务逻辑的场景(例如:订单创建 → 支付 → 发货)。在这里插入图片描述

一、消息顺序性被破坏的原因

  1. 生产者异步/并行发送:消息可能以不同顺序到达MQ。
  2. MQ的分区/队列机制:消息被分散到不同分区或队列,不同队列的消费速度不一致。
  3. 消费者并行消费:多个消费者实例或线程同时处理消息,导致乱序。

二、保证消息顺序性的核心方案

核心原则将需要顺序处理的消息路由到同一个队列(或分区),并由单线程顺序消费

1. 生产者保证消息路由到同一队列
  • 业务标识路由:将同一业务标识(如订单ID、用户ID)的消息通过相同的路由键(如哈希取模)发送到同一个队列。

  • Kafka:为消息指定相同的 Key,相同 Key 的消息会进入同一个分区或者发送消息时将同一业务的消息指定到同一个分区partition

//指定分区 0
kafkaTemplate.send("kafka=topic", 0, "key-001", "value-0001");

//相同业务key  key-001
kafkaTemplate.send("kafka=topic", "key-001", "value-0001");
  • RocketMQ:使用 MessageQueueSelector 自定义队列选择逻辑,确保同一业务的消息进入同一队列。
SendResult sendResult = 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);
2. MQ服务端维护队列顺序
  • 分区/队列内有序:MQ需保证单个分区或队列内消息的存储和投递顺序与发送顺序一致。
  • 限制:Kafka分区、RocketMQ队列默认保证分区/队列内消息顺序。
3. 消费者单线程顺序消费
  • 单线程消费:消费者对同一队列的消息使用单线程处理,避免并发导致的乱序。
  • 示例
    • Kafka:每个分区仅由一个消费者线程处理,天生就是单线程的。
    • RocketMQ:使用 MessageListenerOrderly 监听器顺序消费。
  • 代码示例(RocketMQ消费者)
    consumer.registerMessageListener(new MessageListenerOrderly() {
        @Override
        public ConsumeOrderlyStatus consumeMessage(List<MessageExt> messages, ConsumeOrderlyContext context) {
            // 单线程处理消息
            return ConsumeOrderlyStatus.SUCCESS;
        }
    });
    
4. 失败重试不破坏顺序
  • 顺序消费的重试机制:若某条消息消费失败,需阻塞后续消息处理,直到当前消息成功。
  • 示例:RocketMQ在顺序消费模式下,失败时会重试当前消息,后续消息需等待。

三、不同MQ的实现差异

消息队列顺序性支持关键配置
Kafka分区内顺序保证相同Key的消息发送到同一分区
RocketMQ队列内顺序保证(需使用顺序消息API)MessageListenerOrderly + 队列选择器
RabbitMQ无原生支持,需通过单队列+单消费者模拟顺序性单一队列 + 单消费者线程(synchronized

四、注意事项

  1. 性能与扩展性:顺序性会牺牲并行度,可通过增加队列/分区数量横向扩展(不同业务标识分散到不同队列)。
  2. 全局顺序性:需所有消息进入同一队列(如Kafka单分区),但会严重限制吞吐量,通常不建议。
  3. 业务设计:仅在必要场景(如订单链路)启用顺序性,其他场景尽量允许乱序。

五、总结

1. 保证消息顺序性的核心步骤:

  • . 生产者:按业务标识将消息路由到同一队列。
  • . MQ服务端:确保队列内消息存储有序。
  • . 消费者:单线程消费队列,失败时阻塞重试。
    通过合理设计业务标识和MQ配置,可以在分布式系统中高效实现局部顺序性,平衡一致性与性能。

2. 不同MQ如何选择

三种MQ相比较而言,RocketMQ更适合顺序消费的业务场景,总结如下:

  • . RabbitMQ需要设定交换机Exchange与队列Queue的绑定关系,并且一个队列只对应一个消费者Consumer才可以保证顺序消费,但是队列中的消息被消费者拉去后会从队列删除,如果消息消费失败,重试时会重新入队,消息的顺序就打乱了。
  • . Kafka虽然可以实现分区顺序消费但是在消息失败时,并不会锁住整个partition分区,该消息之后的消息还是会被消费,顺序也就打乱了,顺序消费的设计并没有RocketMQ那么完善。
  • . RocketMQ使用顺序发送,并结合队列选择器可以将同一业务消息发送到同一个队列,再结合MessageListenerOrderly监听器,保证生产者发送顺序和队列存储顺序以及消费者消费消息一致,并且消费失败时,会返回SUSPEND_CURRENT_QUEUE_A_MOMENT状态,阻塞队列一段时间(因为有队列锁),之后会从失败处开始再次消费。

RocketMQ顺序消费实现机制参考链接:https://blog.csdn.net/m0_71845127/article/details/145990210


http://www.kler.cn/a/573775.html

相关文章:

  • 如何在Windows下离线部署DeepSeek并以WebApi形式调用
  • Golang的代码注释规范指南
  • Macro Bullion:国际金价回调态势下,金市表现与金银交易建议
  • 数据结构与算法:回溯(下):子集相关力扣题(78.子集、90.子集Ⅱ、491.非递减子序列)、排列相关力扣题(46.全排列、47.全排列Ⅱ)
  • es如何进行refresh?
  • 鸿蒙NEXT开发-端云一体化开发
  • Python 爬取唐诗宋词三百首
  • 由麻省理工学院计算机科学与人工智能实验室等机构创建低成本、高效率的物理驱动数据生成框架,助力接触丰富的机器人操作任务
  • 闭包:前端开发的“记忆胶囊“与Vue框架的“隐身特工“
  • ANI AGI ASI的区别
  • python学习第三天
  • C# Enumerable类 之 数据(类型)转换
  • 【菜笔cf刷题日常-1600】C. Balanced Stone Heaps(二分求min/max)
  • 整除分块 2025牛客寒假算法基础集训营3G
  • Kotlin 协程(三)协程的常用关键字使用及其比较
  • Visual Stdio 2022 C#调用DeepSeek API
  • HCIA—IP路由静态
  • koa-session设置Cookie后获取不到
  • C#调用Ni板卡进行实现采集任务(模拟量输入输出)示例1
  • 涨薪技术|持续集成Git使用详解