Skip to content

投递语义

本页结论: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 唯一键)是这个窗口的唯一防线——详见消费者崩溃与重投实验

保证成立的条件

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

不保证什么

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

实验复现命令

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

官方资料与版本说明

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

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