跳转到正文

RabbitMQ 可靠性 ​

本页结论:Publisher Confirms 与 Consumer ACK 是互相独立的两段确认;可靠链路 = Confirms + 手动 ACK + 幂等消费。「不丢」永远附带前置条件,exactly-once 不适用于跨系统端到端。

两段确认各管一段 ​

  • Publisher Confirm 只回答「Broker 收到了」:它不保证消息被复制、被持久化到满意程度,更不代表任何消费者处理过。
  • Consumer ACK 只回答「这个消费者处理完了」:与生产端的确认完全独立。
  • 因此「Confirm 成功」不能作为业务成功的通知依据——这是错误表述之一。

生产端:Publisher Confirms ​

java
channel.confirmSelect();
channel.basicPublish(...);
if (!channel.waitForConfirms(5000)) { /* nack:重试或落库补偿 */ }
  • 逐条确认简单可靠;批量模式(waitForConfirms(long) 配合未确认计数)吞吐更高。
  • Broker 返回 nack(或超时)时必须处理:重发或转入本地补偿(Outbox)。重发可能造成重复。
  • 未开 Confirms 时,basicPublish 返回不报错不代表 Broker 收到(路由不到队列的消息会被静默丢弃或 basicReturn)。

消费端:ACK 模式 ​

模式行为风险
自动 ACK(autoAck=true)deliver 即视为完成处理中崩溃 = 丢消息;at-most-once
手动 ACKACK 才移除忘记 ACK 会造成重投与 Unacked 堆积
NACK/Reject + requeue立即重回队头毒消息无限循环、顺序抖动
NACK + DLX交给死信路由需要预先配置 DLX 与 DLQ

Prefetch 与 ACK 一起决定在途消息上限;崩溃后未 ACK 消息自动重投(redelivered=true)。

崩溃窗口与幂等消费(§5.4 基准实现) ​

正确顺序:业务事务提交 → ACK。

text
1. 开启本地数据库事务
2. 插入 messageId 到 processed_messages(唯一键)
3. 唯一键冲突 → duplicate_skipped,安全 ACK
4. 首次处理 → 执行业务写入,提交事务
5. 提交成功后才 basicAck

第 4 步提交成功、第 5 步 ACK 前崩溃 → Broker 必然重投 → 幂等表拦截。该窗口不可消除,只能靠幂等表兜底。完整可复现实验:

bash
bash demos/rabbitmq/consumer-crash/run.sh
rabbitmq / consumer-crashbash demos/rabbitmq/consumer-crash/run.sh
assert.out.txt
PASS crashExitCode: 137
PASS crashAfterBusinessCommit: 1
PASS receivedTotal: 4
PASS redeliveredCount: 1
PASS duplicatesObserved: 1
PASS uniqueMessageIds: 3
PASS business_rows: 3
PASS queueDepthAfter: 0
PASS consumer-run2.exitCode: 0
RESULT: all assertions passed
consumer-run1.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[consumer] timestamp=2026-10-07T10:27:14.314008641Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.crash consumer=consumer-1 attempt=1 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:27:14.322479817Z checkpoint=after-delivery messageId=01JMQ000000000000000000001 token=4fac7be9f8ad-1-after-delivery-01JMQ000000000000000000001-1 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:15.327335311Z checkpoint=after-delivery messageId=01JMQ000000000000000000001 token=4fac7be9f8ad-1-after-delivery-01JMQ000000000000000000001-1 status=released
[consumer] timestamp=2026-10-07T10:27:15.337218764Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.crash attempt=1 status=business_committed
[replay-gate] timestamp=2026-10-07T10:27:15.337699711Z checkpoint=before-ack messageId=01JMQ000000000000000000001 token=4fac7be9f8ad-1-before-ack-01JMQ000000000000000000001-2 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:16.340446936Z checkpoint=before-ack messageId=01JMQ000000000000000000001 token=4fac7be9f8ad-1-before-ack-01JMQ000000000000000000001-2 status=released
[consumer] timestamp=2026-10-07T10:27:16.340660034Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.crash status=crash_injected
consumer-run2.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[consumer] timestamp=2026-10-07T10:27:18.331903782Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.crash consumer=consumer-1 attempt=1 redelivered=true status=received
[replay-gate] timestamp=2026-10-07T10:27:18.340119726Z checkpoint=after-delivery messageId=01JMQ000000000000000000001 token=2cbfb1260f3d-1-after-delivery-01JMQ000000000000000000001-1 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:19.244860771Z checkpoint=after-delivery messageId=01JMQ000000000000000000001 token=2cbfb1260f3d-1-after-delivery-01JMQ000000000000000000001-1 status=released
[consumer] timestamp=2026-10-07T10:27:19.251586332Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.crash attempt=1 status=duplicate_skipped
[replay-gate] timestamp=2026-10-07T10:27:19.251975513Z checkpoint=before-ack messageId=01JMQ000000000000000000001 token=2cbfb1260f3d-1-before-ack-01JMQ000000000000000000001-2 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:20.253760487Z checkpoint=before-ack messageId=01JMQ000000000000000000001 token=2cbfb1260f3d-1-before-ack-01JMQ000000000000000000001-2 status=released
[consumer] timestamp=2026-10-07T10:27:20.255434279Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000002 eventType=order.created schemaVersion=1 aggregateId=order-1002 traceId=00000000000000000000000000000002 correlationId=order-1002 destination=orders.crash consumer=consumer-1 attempt=1 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:27:20.255920404Z checkpoint=after-delivery messageId=01JMQ000000000000000000002 token=2cbfb1260f3d-1-after-delivery-01JMQ000000000000000000002-3 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:21.257916980Z checkpoint=after-delivery messageId=01JMQ000000000000000000002 token=2cbfb1260f3d-1-after-delivery-01JMQ000000000000000000002-3 status=released
[consumer] timestamp=2026-10-07T10:27:21.263174006Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000002 eventType=order.created schemaVersion=1 aggregateId=order-1002 traceId=00000000000000000000000000000002 correlationId=order-1002 destination=orders.crash attempt=1 status=business_committed
[replay-gate] timestamp=2026-10-07T10:27:21.263516787Z checkpoint=before-ack messageId=01JMQ000000000000000000002 token=2cbfb1260f3d-1-before-ack-01JMQ000000000000000000002-4 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:22.165093163Z checkpoint=before-ack messageId=01JMQ000000000000000000002 token=2cbfb1260f3d-1-before-ack-01JMQ000000000000000000002-4 status=released
[consumer] timestamp=2026-10-07T10:27:22.166557146Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000003 eventType=order.created schemaVersion=1 aggregateId=order-1003 traceId=00000000000000000000000000000003 correlationId=order-1003 destination=orders.crash consumer=consumer-1 attempt=1 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:27:22.166974929Z checkpoint=after-delivery messageId=01JMQ000000000000000000003 token=2cbfb1260f3d-1-after-delivery-01JMQ000000000000000000003-5 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:23.168666227Z checkpoint=after-delivery messageId=01JMQ000000000000000000003 token=2cbfb1260f3d-1-after-delivery-01JMQ000000000000000000003-5 status=released
[consumer] timestamp=2026-10-07T10:27:23.170617117Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000003 eventType=order.created schemaVersion=1 aggregateId=order-1003 traceId=00000000000000000000000000000003 correlationId=order-1003 destination=orders.crash attempt=1 status=business_committed
[replay-gate] timestamp=2026-10-07T10:27:23.171131674Z checkpoint=before-ack messageId=01JMQ000000000000000000003 token=2cbfb1260f3d-1-before-ack-01JMQ000000000000000000003-6 status=waiting
[replay-gate] timestamp=2026-10-07T10:27:24.273522547Z checkpoint=before-ack messageId=01JMQ000000000000000000003 token=2cbfb1260f3d-1-before-ack-01JMQ000000000000000000003-6 status=released
[consumer] timestamp=2026-10-07T10:27:24.274089233Z level=INFO service=order-service product=rabbitmq lab=consumer-crash queue=orders.crash received=3 status=done
inspect-db.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[inspect] timestamp=2026-10-07T10:27:24.716719592Z level=INFO service=order-service product=rabbitmq lab=consumer-crash business_rows=3 processed_rows=3 status=snapshot
producer.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[producer] timestamp=2026-10-07T10:27:13.597266852Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.crash routingKey=orders.crash durationMs=2 status=confirmed
[producer] timestamp=2026-10-07T10:27:13.599968622Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000002 eventType=order.created schemaVersion=1 aggregateId=order-1002 traceId=00000000000000000000000000000002 correlationId=order-1002 destination=orders.crash routingKey=orders.crash durationMs=0 status=confirmed
[producer] timestamp=2026-10-07T10:27:13.600894393Z level=INFO service=order-service product=rabbitmq lab=consumer-crash messageId=01JMQ000000000000000000003 eventType=order.created schemaVersion=1 aggregateId=order-1003 traceId=00000000000000000000000000000003 correlationId=order-1003 destination=orders.crash routingKey=orders.crash durationMs=0 status=confirmed
[producer] timestamp=2026-10-07T10:27:13.600975954Z level=INFO service=order-service product=rabbitmq lab=consumer-crash destination=orders.crash confirmed=3 status=done
setup.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

实验分两轮运行:第一轮在首条消息业务提交后、ACK 前以退出码 137 终止;第二轮恢复消费者,同一 messageId 以 redelivered=true 再次到达并被幂等表拦截。核心断言为:3 条唯一消息、4 次总投递、1 次重投、1 次 duplicate_skipped、业务表仍只有 3 行。在 RabbitMQ 实验台查看崩溃与恢复轨道。

失败路径:重试与 DLQ ​

RabbitMQ 无内置消费重试。推荐模式(本仓库 retry-dlq 实验验证):

  • 工作队列 DLX → 重试队列(TTL 延迟)→ 回环到工作队列;
  • 消费者用 x-death 计数限制最大尝试次数;
  • 超限显式投递 DLQ 并告警。

本仓库用 orders.work、orders.retry 和 orders.dlq 验证这条路径:工作队列拒绝消息后经 DLX 进入带 1 秒 TTL 的重试队列,到期再由 DLX 回到工作队列;消费者根据 x-death 计算尝试次数,第 3 次失败后把毒消息隔离到 DLQ。正常的两条消息照常落库,毒消息的尝试次数为 1、2、3,最终 business_rows=2、dlqMessages=1。

bash
bash demos/rabbitmq/retry-dlq/run.sh
rabbitmq / retry-dlqbash demos/rabbitmq/retry-dlq/run.sh
assert.out.txt
PASS confirmed: 3
PASS business_rows: 2
PASS poisonAttempts: 1,2,3
PASS poisonMovedToDlq: 1
PASS dlqMessages: 1
PASS workQueueDepthAfter: 0
PASS retryQueueDepthAfter: 0
PASS consumer.exitCode: 0
RESULT: all assertions passed
consumer.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[consumer] timestamp=2026-10-07T10:28:15.088784369Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.work consumer=consumer-1 attempt=1 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:28:15.101327476Z checkpoint=after-delivery messageId=01JMQ000000000000000000001 token=fb78909d7c14-1-after-delivery-01JMQ000000000000000000001-1 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:16.408009254Z checkpoint=after-delivery messageId=01JMQ000000000000000000001 token=fb78909d7c14-1-after-delivery-01JMQ000000000000000000001-1 status=released
[consumer] timestamp=2026-10-07T10:28:16.419738210Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.work attempt=1 status=business_committed
[replay-gate] timestamp=2026-10-07T10:28:16.420247190Z checkpoint=before-ack messageId=01JMQ000000000000000000001 token=fb78909d7c14-1-before-ack-01JMQ000000000000000000001-2 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:17.722595981Z checkpoint=before-ack messageId=01JMQ000000000000000000001 token=fb78909d7c14-1-before-ack-01JMQ000000000000000000001-2 status=released
[consumer] timestamp=2026-10-07T10:28:17.724756019Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ000000000000000000002 eventType=order.created schemaVersion=1 aggregateId=order-1002 traceId=00000000000000000000000000000002 correlationId=order-1002 destination=orders.work consumer=consumer-1 attempt=1 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:28:17.725346081Z checkpoint=after-delivery messageId=01JMQ000000000000000000002 token=fb78909d7c14-1-after-delivery-01JMQ000000000000000000002-3 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:19.130247171Z checkpoint=after-delivery messageId=01JMQ000000000000000000002 token=fb78909d7c14-1-after-delivery-01JMQ000000000000000000002-3 status=released
[consumer] timestamp=2026-10-07T10:28:19.132336747Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ000000000000000000002 eventType=order.created schemaVersion=1 aggregateId=order-1002 traceId=00000000000000000000000000000002 correlationId=order-1002 destination=orders.work attempt=1 status=business_committed
[replay-gate] timestamp=2026-10-07T10:28:19.132789332Z checkpoint=before-ack messageId=01JMQ000000000000000000002 token=fb78909d7c14-1-before-ack-01JMQ000000000000000000002-4 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:20.636430076Z checkpoint=before-ack messageId=01JMQ000000000000000000002 token=fb78909d7c14-1-before-ack-01JMQ000000000000000000002-4 status=released
[consumer] timestamp=2026-10-07T10:28:20.637943339Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work consumer=consumer-1 attempt=1 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:28:20.638491974Z checkpoint=after-delivery messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-after-delivery-01JMQ00000000000000000000P-5 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:22.040822172Z checkpoint=after-delivery messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-after-delivery-01JMQ00000000000000000000P-5 status=released
[consumer] timestamp=2026-10-07T10:28:22.041904172Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work attempt=1 status=retry
[replay-gate] timestamp=2026-10-07T10:28:22.042433650Z checkpoint=before-retry messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-before-retry-01JMQ00000000000000000000P-6 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:23.344547194Z checkpoint=before-retry messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-before-retry-01JMQ00000000000000000000P-6 status=released
[consumer] timestamp=2026-10-07T10:28:24.347460284Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work consumer=consumer-1 attempt=2 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:28:24.348008177Z checkpoint=after-delivery messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-after-delivery-01JMQ00000000000000000000P-7 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:25.651611504Z checkpoint=after-delivery messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-after-delivery-01JMQ00000000000000000000P-7 status=released
[consumer] timestamp=2026-10-07T10:28:25.652492668Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work attempt=2 status=retry
[replay-gate] timestamp=2026-10-07T10:28:25.652931477Z checkpoint=before-retry messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-before-retry-01JMQ00000000000000000000P-8 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:27.355753599Z checkpoint=before-retry messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-before-retry-01JMQ00000000000000000000P-8 status=released
[consumer] timestamp=2026-10-07T10:28:28.358190936Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work consumer=consumer-1 attempt=3 redelivered=false status=received
[replay-gate] timestamp=2026-10-07T10:28:28.358640906Z checkpoint=after-delivery messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-after-delivery-01JMQ00000000000000000000P-9 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:29.661027280Z checkpoint=after-delivery messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-after-delivery-01JMQ00000000000000000000P-9 status=released
[replay-gate] timestamp=2026-10-07T10:28:29.662056362Z checkpoint=before-dlq messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-before-dlq-01JMQ00000000000000000000P-10 status=waiting
[replay-gate] timestamp=2026-10-07T10:28:31.064384155Z checkpoint=before-dlq messageId=01JMQ00000000000000000000P token=fb78909d7c14-1-before-dlq-01JMQ00000000000000000000P-10 status=released
[consumer] timestamp=2026-10-07T10:28:31.066674421Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work attempt=3 status=poison_to_dlq
[consumer] timestamp=2026-10-07T10:28:31.067017057Z level=INFO service=order-service product=rabbitmq lab=retry-dlq queue=orders.work received=5 status=done
inspect-db.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[inspect] timestamp=2026-10-07T10:28:31.977614051Z level=INFO service=order-service product=rabbitmq lab=retry-dlq business_rows=2 processed_rows=2 status=snapshot
producer.out.txt
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[producer] timestamp=2026-10-07T10:28:14.239167174Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ000000000000000000001 eventType=order.created schemaVersion=1 aggregateId=order-1001 traceId=00000000000000000000000000000001 correlationId=order-1001 destination=orders.work routingKey=orders.work durationMs=7 status=confirmed
[producer] timestamp=2026-10-07T10:28:14.243278287Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ000000000000000000002 eventType=order.created schemaVersion=1 aggregateId=order-1002 traceId=00000000000000000000000000000002 correlationId=order-1002 destination=orders.work routingKey=orders.work durationMs=0 status=confirmed
[producer] timestamp=2026-10-07T10:28:14.244510557Z level=INFO service=order-service product=rabbitmq lab=retry-dlq messageId=01JMQ00000000000000000000P eventType=order.created schemaVersion=1 aggregateId=order-poison traceId=0000000000000000000000000000000P correlationId=order-poison destination=orders.work routingKey=orders.work durationMs=0 status=confirmed
[producer] timestamp=2026-10-07T10:28:14.244652672Z level=INFO service=order-service product=rabbitmq lab=retry-dlq destination=orders.work confirmed=3 status=done

在 RabbitMQ 实验台查看重试与 DLQ 轨道。

顺序与重试的关系 ​

  • 单队列 FIFO 只在「无 requeue、无乱序 ACK」时成立。
  • NACK+requeue 会把失败消息送回队头或原位置之后(取决于实现与配置),重试消息与后续消息的相对顺序可能变化。
  • 需要「同 orderId 严格有序」时:routing key=orderId 绑定到专属队列 + 单消费者 + 失败进 DLQ 而非 requeue。

三层语义总结 ​

层级保证条件
Broker消息被接受/保留Confirms 开启;Quorum Queue 多数派写入;或单节点存活且已持久化
Client不重发丢、不提前 ACKwaitForConfirms 处理 nack;手动 ACK + prefetch
Business效果恰好一次幂等表 + 本地事务;Outbox 保证「业务成功必发出」

常见误区 ​

  • 「Confirm + ACK 都开了就是 exactly-once」——端到端仍可能重复(崩溃窗口),必须幂等。
  • 「自动 ACK 配 durable 队列也安全」——丢消息窗口在消费侧,durable 管不到。
  • 「重试次数靠 Broker」——RabbitMQ 不提供,需要应用 + x-death 或重试队列实现。

官方资料 ​

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