MQ保证消息的顺序性

embedded/2025/3/6 9:22:08/

在消息队列(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() {@Overridepublic 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() {@Overridepublic 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.ppmy.cn/embedded/170447.html

相关文章

CSS Selectors

当然&#xff0c;理解纯CSS选择器&#xff08;CSS Selectors&#xff09;对于进行UI自动化测试非常重要。CSS选择器允许您通过元素的属性、层级关系、类名、ID等来精准定位页面上的元素。下面我将详细讲解CSS选择器的常见用法&#xff0c;并结合您的需求提供具体的示例。 1. 基…

AI浪潮下的软件工程师:如何在变革中突破自我,掌握AI技术

AI浪潮下的软件工程师&#xff1a;如何在变革中突破自我&#xff0c;掌握AI技术 引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;各行各业都在经历前所未有的变革。软件工程师作为技术领域的核心力量&#xff0c;面临着新的挑战和机遇。本文将探讨在…

Docker01 - docker快速入门

Docker快速入门 文章目录 Docker快速入门一&#xff1a;Docker概述1&#xff1a;虚拟机技术和容器化技术2&#xff1a;Docker名词解释2.1&#xff1a;Docker镜像(images)2.2&#xff1a;Docker容器(containers)2.3&#xff1a;Docker仓库(registry) 3&#xff1a;Docker下载安装…

【LeetCode226】翻转二叉树

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 思路与算法 这个问题自然是递归的&#xff0c;因为反转一棵树涉及到反转它的子树。 让 f(node) 是一个函数&#xff0c;用于反转以 node 为根的二叉树。如果 node 有左子树 L…

三参数水质在线分析仪:从源头保障饮用水安全

【TH-ZS03】饮用水安全是人类健康的重要保障&#xff0c;其质量直接关系到人们的生命健康。随着工业化、城市化的快速发展&#xff0c;水体污染问题日益严峻&#xff0c;饮用水安全面临着前所未有的挑战。为了从源头保障饮用水安全&#xff0c;科学、高效的水质监测手段必不可少…

VTK知识学习(45)- 基本的图形操作(四)

1、点云配准 1&#xff09;概述 在计算机逆向工程中&#xff0c;通过三维扫描等实物数字化技术可以获取各种点云数据。但是受测量环境和设备的影响&#xff0c;在一次测量的情况下&#xff0c;难以获取实物整体的点云数据&#xff0c;因此需要多次从不同角度进行测量。但不同的…

面试基础--Spring Boot启动流程及源码实现

深度解析Spring Boot启动流程及源码实现 一、Spring Boot启动全景图&#xff08;含核心阶段&#xff09; #mermaid-svg-dYTQ6WPa3o6vKFHh {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dYTQ6WPa3o6vKFHh .error-i…

http status是什么?常见的http状态码指的是什么意思?

HTTP 状态码 HTTP 状态码&#xff08;HTTP Status Code&#xff09;是服务器在响应客户端请求时返回的一个三位数字代码&#xff0c;用于表示请求的处理结果。HTTP 状态码是 HTTP 协议的一部分&#xff0c;帮助客户端&#xff08;如浏览器或应用程序&#xff09;了解请求是否成…