跳转到正文

💬 Hello MQ学习消息队列的核心概念、常用产品与实践方法。

8 个主流 MQ · 统一实验验证 · 三层语义说明 · 横向选型矩阵

🎯 典型消息系统快速入口 ​

前 5 个为高频使用场景的代表性产品,其余 3 个可在导航栏「更多」下拉中查看。

产品类型核心价值分卷文档
RabbitMQ 🐰Queue灵活路由、可靠投递、延迟/优先级队列8 页详解 →
Apache Kafka 🦓Log分区持久日志、高吞吐事件流、实时数据处理8 页详解 →
RocketMQ 🚀Queue-Log事务消息、定时/延迟消息、金融级可靠性8 页详解 →
Apache Pulsar 🦋Log-Cloud存算分离、多租户、云原生原生设计8 页详解 →
Redis Streams 💾Stream内存高性能、简单 API、嵌入式事件总线8 页详解 →

🔄 从同步调用到事件驱动 ​

切换下面的模式,观察调用关系如何从「点对点强耦合」演化为「经 Broker 解耦」;点「播放消息流」可高亮一条消息的流转路径。

交互拓扑:同步调用 → 事件驱动

订单服务逐个直调下游:任一环节慢或挂,整条链路一起慢、一起挂。

订单服务库存服务积分服务通知服务
  • 订单服务 → 库存服务 (HTTP,阻塞等待)
  • 订单服务 → 积分服务 (HTTP,阻塞等待)
  • 订单服务 → 通知服务 (HTTP,阻塞等待)

模型细节与产品对照见消息模型。


📊 关键能力横向对比 ​

七个产品在核心功能上的支持方式不同——「原生」不等于「免费」,「业务实现」也不等于「不可行」。

能力RabbitMQKafkaRocketMQPulsarRedis StreamsNATSArtemis
端到端 exactly-once业务实现仅集群内 EOS业务实现业务实现业务实现业务实现业务实现
顺序消息单队列内分区内MessageGroup 内分区 + 订阅类型相关单 Stream 内Subject/Stream 内单队列内 + Message Group
内置重试与 DLQ组合配置(TTL+DLX)业务实现原生(Broker 内置)组合配置(DeadLetterPolicy)业务实现(PEL+XCLAIM)组合配置(AckWait+MaxDeliver)原生(address-setting)
延迟消息组合配置(TTL+DLX)业务实现原生(定时投递)业务实现不适用原生(JetStream 延迟投递)原生(_AMQ_SCHED_DELAY)
消息回放不适用(ACK 即删)原生(位点/时间戳)原生(位点重置)原生(reset-cursor)原生(XRANGE/XGROUP SETID)Core 不适用;JetStream 原生不适用(ack 即删)

选型没有万能冠军:按输入维度筛选候选,见 选型指南。


🚀 本地实验 Quick Start ​

bash
git clone https://github.com/xy2401/hello-mq.git && cd hello-mq
npm install

# 运行单个实验
bash demos/rabbitmq/basic/run.sh                 # 启动 RabbitMQ,发 3 条、收 3 条并校验

# 运行全部实验
for s in demos/*/*/run.sh; do bash "$s"; done    # 15 个实验全跑一遍

环境要求与完整说明见实验约定。

适合目标读者:需要深入理解消息系统底层原理、构建高可靠分布式系统的后端工程师与 SRE。


消息系统基础概念 ​

为什么需要异步消息 ​

本页结论:异步消息解决的是解耦、削峰与最终一致性三类问题,代价是引入投递不确定性,需要显式的可靠性设计。

适用场景 ​

继续增加同步 RPC 无法解决以下问题:

  1. 调用链耦合:下单接口同步调用库存、积分、通知,任一下游故障都会拖垮主流程。
  2. 峰值压力:秒杀瞬时流量远超下游处理能力,同步调用只能靠拒绝请求兜底。
  3. 长耗时副作用:发送邮件、生成报表、调用第三方 API 不应阻塞用户请求。
  4. 事件广播:多个下游系统都需要“订单已创建”这一事实,生产者不应逐一维护订阅关系。

核心模型 ​

生产者把消息交给 Broker 后即可返回;Broker 承担保管与转发职责;消费者按自己的能力处理。这是从“同步调用链”到“事件驱动协作”的本质变化。

最小配置 ​

引入消息系统的最小代价清单:

  • 为每类事件定义契约:唯一标识、事件类型、Schema 版本、追踪字段。
  • 明确投递语义:允许重复就必须有幂等消费;不允许丢失就必须开启生产确认与消费确认。
  • 建立观测:发送确认延迟、消费积压、重投递率。

不保证什么 ​

  • 异步消息不是免费的:端到端延迟增加、系统状态更难推理、需要额外的 Broker 运维。
  • 引入 Broker 不会自动获得“不丢消息”:默认配置下多数产品存在丢失窗口(见投递语义)。
  • 它也不替代事务:跨服务的数据一致性仍需 Outbox、幂等消费、Saga 等模式(后续分卷覆盖)。

常见误区 ​

  • “上了 MQ 就解耦了”——如果消费者仍然依赖生产者的接口契约或共享数据库,解耦只是形式上的。
  • “异步一定更快”——单条消息的端到端延迟通常变高;收益在吞吐与可用性,不在单请求延迟。

实验复现命令 ​

bash
bash demos/rabbitmq/basic/run.sh   # 观察生产确认、投递、消费确认三个独立状态

官方资料与版本说明 ​

本页为产品无关的原理性内容,不依赖特定产品版本;各产品差异见对应分卷与官方资料基线。

消息模型 ​

本页结论:队列、发布订阅、分区日志、请求响应四种模型解决不同问题;选型第一步是确定需要的模型,而不是挑选产品。

四种基本模型 ​

下面提供可切换的交互拓扑:选择模型查看参与方与消息流向,点「播放消息流」高亮一条消息的路径(静态示意,不连接真实 Broker)。

一条消息只被组内一个消费者处理;消费者越多,处理能力越强(RabbitMQ Queue / Kafka 同组消费者 / Pulsar Shared)。

生产者Queue / 分区消费者 A消费者 B
  • 生产者 → Queue / 分区 (入队)
  • Queue / 分区 → 消费者 A (二选一投递)
  • Queue / 分区 → 消费者 B (二选一投递)
1. 竞争消费(Work Queue / Competing Consumers) ​

一条消息只被组内一个消费者处理,消费者水平扩展处理能力。

典型场景:任务分发、订单处理。关键语义:分发公平性、预取(Prefetch)、单条消息只成功处理一次(配合确认)。

2. 发布订阅(Pub/Sub) ​

一条消息被每个订阅各投递一次;订阅内部可以再竞争消费。

典型场景:事件广播。关键语义:每个订阅独立维护消费位置;新增订阅通常只能消费其建立之后的消息(日志型系统可回放除外)。

3. 分区日志(Partitioned Log / Event Stream) ​

消息追加写入分区,消费不删除数据,按游标(Offset/Cursor)推进,可任意回放。

典型场景:事件溯源、审计流、流处理上游。关键语义:分区内有序、消费组再均衡、保留期与回放。

4. 请求响应(Request/Reply) ​

借助消息通道完成一问一答,通常有临时回复队列或关联 ID。适合需要跨进程调用但不想直连的场景;不适合要求严格低延迟的同步调用。

保证成立的条件 ​

  • 竞争消费的“每条只处理一次”依赖消费确认 + 业务幂等(at-least-once 下重复投递是常态)。
  • 发布订阅的“每个订阅都能收到”依赖各订阅自身的确认与重试配置。
  • 分区日志的“可回放”依赖保留期(Retention)未被清理。

不保证什么 ​

  • 队列模型的“消费即删”不提供历史回放;不要把它当日志用。
  • 日志模型的“高吞吐”不代表低延迟场景同样合适。
  • 请求响应模式需要自行处理超时与关联,Broker 不提供 RPC 框架的完整语义。

常见误区 ​

  • 把 RabbitMQ 的 Topic Exchange(路由机制)与 Kafka 的 Topic(订阅通道+日志)当作同一概念。
  • 认为发布订阅天然广播给“所有消费者”——实际单位是订阅,同一订阅内的多个消费者仍是竞争消费。

实验复现命令 ​

bash
bash demos/rabbitmq/basic/run.sh     # 竞争消费:发 3 收 3,单队列单消费者
bash demos/rabbitmq/routing/run.sh   # 三个独立订阅(队列)对同一交换机的不同路由

官方资料与版本说明 ​

模型定义为中性描述;产品映射见 RabbitMQ 概念映射 与统一术语表。

投递语义 ​

本页结论:at-most-once / at-least-once / exactly-once 描述的是“哪一段链路”的保证;生产确认与消费确认是两段独立的确认,任何“不丢”结论都必须附带前置条件与故障窗口。

三段链路 ​

语义含义代价
at-most-once(至多一次)不重投;发送或处理失败时消息可能丢失最低延迟,可能丢消息
at-least-once(至少一次)不丢失;失败时重投,业务必须预期重复需要幂等消费或去重
exactly-once(恰好一次)效果上每条消息只被应用一次仅覆盖特定边界,见下

三层语义说明法 ​

每项“保证”必须拆开看:

层级要回答的问题
Broker 层什么条件下接受、持久化、复制或重投消息?(确认级别、副本条件、队列持久化)
Client 层SDK 超时、重试、ACK、Offset 提交如何配置?超时后重试可能造成重复
Business 层数据库写入与外部副作用如何保持一致?Outbox + 幂等消费

关键纠偏 ​

  • at-least-once 意味着业务必须预期重复,不是“偶尔可能重复”。消费者崩溃窗口(处理成功但确认前崩溃)必然产生重投。
  • exactly-once 有边界。例如 Kafka 的事务性 exactly-once 覆盖“Kafka 内部读取-处理-写入”;写外部数据库仍需幂等设计。不要把 Broker 事务夸大为跨系统事务。
  • 生产确认 ≠ 消费处理。Publisher Confirm 只表示 Broker 承担了保管责任。
  • “已消费”可能指 ACK、Offset 已提交或游标前移,不一定代表业务副作用成功。

故障窗口示例:确认后、业务前崩溃 ​

幂等表(processed_messages 唯一键)是这个窗口的唯一防线——详见 RabbitMQ 可靠性。

保证成立的条件 ​

  • 不丢消息(生产侧):开启生产确认 + Broker 持久化/复制条件满足后才算发送成功。
  • 不丢消息(消费侧):手动确认,业务成功后才 ACK/提交 Offset。
  • 端到端“效果恰好一次”:at-least-once + 幂等消费(业务唯一键),这是工程上的通用做法。

不保证什么 ​

  • 默认配置下(自动 ACK、无确认发送)任何产品都不保证不丢。
  • exactly-once 不覆盖消息之外的任意外部副作用(邮件、第三方 API)。

实验复现命令 ​

bash
bash demos/rabbitmq/consumer-crash/run.sh   # 重投发生且被幂等拦截:duplicatesObserved=1, duplicatesApplied=0

官方资料与版本说明 ​

各产品的确认机制与配置见 RabbitMQ 可靠性;官方来源见官方资料基线(checkedAt: 2026-08-19)。

顺序语义 ​

本页结论:消息系统只承诺“某个范围内”的顺序(分区/队列/Key/单消费者);全局顺序代价极高,失败重试必然与顺序冲突。

顺序的层级 ​

层级含义代价
全局顺序(Total Order)所有消息按发送顺序被所有消费者看到只能单队列/单分区/单消费者,吞吐受限
队列/分区内顺序同一队列或分区内按写入顺序消费需要把相关消息路由到同一单元
Key 顺序同一业务 Key(如 orderId)的消息有序由路由/分区键保证,跨 Key 无序
单消费者顺序单个消费者按接收顺序处理并发处理即失效

核心模型 ​

把需要有序的消息映射到同一顺序单元,是顺序设计的通用做法:

  • 队列型产品(如 RabbitMQ):单队列 + 单消费者 + prefetch=1 才能得到处理顺序;多消费者竞争消费时处理顺序不保证。
  • 日志型产品(如 Kafka/Pulsar):以分区键决定局部顺序;消费组内一个分区只被一个消费者处理。

失败重试与顺序的冲突 ​

顺序消费中一条消息失败会阻塞后续消息(头阻塞,Head-of-line Blocking)。两种取舍:

  1. 阻塞重试:保住顺序,牺牲进度;重试次数必须有限,否则毒消息卡死整个单元。
  2. 跳过/旁路:保住进度,破坏顺序;失败消息进入重试队列或 DLQ,后续消息继续。

保证成立的条件 ​

  • 生产端:相同 Key 的消息必须由同一生产者会话、按序发送(并发发送会破坏进入 Broker 的顺序)。
  • Broker:消息必须落在同一队列/分区;扩缩容、再均衡可能短暂影响(日志型产品分区再分配时可能重复或暂停)。
  • 消费端:单线程处理该单元;prefetch/拉取批量为 1 或串行提交。

不保证什么 ​

  • 单分区/单队列内顺序 不等于端到端业务完成顺序:下游外部调用耗时不同,完成顺序可能颠倒。
  • 重投递的消息可能晚于后续消息被处理(崩溃恢复后),严格顺序场景必须检测并处置。

常见误区 ​

  • “多开消费者更快还能保序”——竞争消费破坏处理顺序。
  • “设置了 Key 就全局有序”——Key 只保证同 Key 局部有序。

实验复现命令 ​

bash
bash demos/rabbitmq/retry-dlq/run.sh   # 观察失败消息旁路对正常消息进度的影响

官方资料与版本说明 ​

中性定义见统一术语表;Kafka/Pulsar 的分区顺序细节在其分卷落地(Phase 2/3)。

存储与回放 ​

本页结论:“消费”是否删除数据是队列模型与日志模型的根本分野;回放能力由保留策略决定,消费位点是回放的操纵杆。

两种存储模型 ​

模型消费后数据回放代表
队列模型(消费即删)确认后删除有限(依赖 TTL/DLX 等旁路)RabbitMQ Classic Queue、RocketMQ
日志模型(消费不删)按保留策略清理保留期内任意位置回放Kafka、Pulsar、Redis Streams

保留策略(Retention) ​

  • 按时间:保留 N 天(如 Kafka retention.ms)。
  • 按大小:总量上限,滚动删除最旧段。
  • 压缩(Log Compaction):日志型产品可只保留每个 Key 的最新值(适合状态快照,不适合事件全量审计)。
  • 分层存储(Tiered Storage):冷数据转移到对象存储以延长可回放窗口(Pulsar/Kafka 生态能力)。

回放的操纵杆:消费位点 ​

  • 日志型:Offset/Cursor 可重置到最早、最新或指定时间戳;消费组彼此独立,回放不影响其他组。
  • 队列型:没有位点概念;“回放”需要生产者重发或借助 DLX 重投。

保证成立的条件 ​

  • 回放可行 ⟺ 消息仍在保留期内 ⟺ 保留策略与磁盘容量匹配。
  • 持久化消息在 Broker 重启后可用,依赖持久化配置与副本条件(各产品分卷说明)。

不保证什么 ​

  • 队列模型的“已消费”消息不可找回(除非业务侧另有事件存储)。
  • 日志模型的“可回放”不承诺无限期:保留期一过即清理;压缩主题丢失同 Key 旧值。
  • 回放会产生消费洪峰,需要评估下游处理能力(见背压与积压)。

常见误区 ​

  • 把 Kafka 的 Topic 当 RabbitMQ 队列用:期望消费即删、按条清理。
  • 假设“消息还在 Broker 里”就能回放——先核对保留策略与位点状态。

实验复现命令 ​

bash
bash demos/rabbitmq/basic/run.sh   # 队列模型:消费确认后队列深度归零,无回放入口

Kafka/Pulsar 的回放实验在 Phase 2/3 落地。

官方资料与版本说明 ​

保留与压缩语义以各产品官方文档为准(官方资料基线,checkedAt: 2026-08-19)。

背压与积压 ​

本页结论:积压(Backlog)是“生产速率 > 消费能力”的信号;先定位成因(生产突增 / 消费变慢 / 消费者离线 / 分区不均 / 毒消息循环 / Broker 限流),再决定扩容、限流还是隔离。

核心指标 ​

模型积压指标
队列型Queue Depth(ready + unacked)、最老消息年龄
日志型Consumer Lag(最新位点 − 消费位点)、各分区 lag 分布
Redis StreamsStream 长度 + Pending Entries List(PEL)数量与滞留时长
PulsarSubscription Backlog

辅助指标:消费速率、处理延迟 P95、重投递率、DLQ 增量、Broker 磁盘/内存水位。

积压定位决策树 ​

处置手段 ​

  1. 水平扩容消费者(竞争消费/消费组模型);日志型注意消费者数 ≤ 分区数。
  2. 限流保护下游:与其把下游打挂,不如让积压停留在 Broker(配合磁盘容量评估)。
  3. 隔离毒消息:有限重试 + DLQ,避免同一条坏消息反复阻塞(见 RabbitMQ 重试与 DLQ)。
  4. 批量与预取调优:增大 prefetch/批大小可提升吞吐,但会拉长单条确认延迟、扩大崩溃重复窗口。
  5. 回放预案:积压清理或位点重置会产生消费洪峰,需预留处理能力。

保证成立的条件 ​

  • 积压告警阈值必须结合业务可容忍延迟设定,而不是只看绝对数量。
  • 扩容有效 ⟺ 顺序约束允许(同一顺序单元不能并行消费,见顺序语义)。

不保证什么 ​

  • Broker 不能无限积压:队列型受内存/磁盘限制(可能触发流控或拒绝发布),日志型受保留策略限制(积压超过保留期会丢未消费消息)。
  • 消费者数量不是越多越好:超过并发单元数(分区数)只是空转。

观测指标清单 ​

  • Producer:发送速率、确认延迟、错误率。
  • Broker:入站/出站速率、存储大小、磁盘/内存水位、连接数。
  • Consumer:消费速率、处理延迟、失败率、重投递率。
  • Backlog:深度/lag、最老消息年龄。
  • DLQ:新增速率、存量、最老消息年龄。

实验复现命令 ​

bash
bash demos/rabbitmq/retry-dlq/run.sh   # 毒消息循环的识别与隔离

积压-恢复实验(消费者离线后追赶)在后续版本补充。

官方资料与版本说明 ​

各产品的流控与积压观测命令见产品分卷运维页(当前:RabbitMQ 运维与观测)。

以统一实验验证消息系统语义边界