🧑💻 面试官:怎样避免消息丢失?
🙋♂️ 我:生产者等确认,队列做持久化,消费者处理完再 ACK。
🧑💻 面试官:生产者已经收到 ACK,订单邮件是不是已经发出去了?
🙋♂️ 我:不是,那是 Broker 的发布确认,不是消费者完成业务。
🧑💻 面试官:如果数据库提交成功,程序却在发送消息前崩溃呢?如果确认只是丢在网络上,重发会不会出现两条消息?
消息可靠性要沿着「整条交接链」来看。某一段确认成功,只能说明这一段满足了对应条件,不能替后面的业务作保证。
面试速答(60 秒版)
消息是否可靠,需要分别检查业务到生产者、生产者到 Broker、Broker 存储与复制,以及消费者处理这几段。
生产者要处理发送失败和确认不确定,Broker 要按产品机制配置持久化、复制与故障恢复。消费者则应在业务处理可靠完成后确认,失败按规则重试或进入死信处理。
发布确认与消费确认不是同一个 ACK。收到发布确认,不代表消费者已经执行;数据库提交以后再发消息,还存在双写间隙,可以考虑本地事务写 Outbox,再可靠投递。
同时,确认丢失和消费者崩溃都可能带来重复消息。系统需要业务幂等、可追踪状态与补偿机制,不能承诺“打开持久化就端到端恰好一次”。

知识点详解:消息在哪些地方可能掉下去?
发布确认,只是第一次交接
生产者调用发送 API,不等于 Broker 已经接住消息。操作可能还在客户端缓冲,也可能因网络中断留下未知结果。
以 RabbitMQ 为例,publisher confirms 用于发布者确认;consumer acknowledgements 用于消费者确认。官方确认文档明确区分了两者。
还需要确认消息是否成功路由。某些情况下,无法路由的消息也可能得到发布确认,需要结合 mandatory 返回等对应机制处理,不能只看“确认成功”就认为指定队列里一定有消息。
对于持久消息与可靠队列,确认时机和复制要求也有具体约定,应按选用的队列类型检查,而不是所有产品都背同一套词。
最容易漏掉的,是数据库与消息之间
假设业务要保存订单,再通知库存服务。
如果先提交数据库,接着进程崩溃,消息还没发送,这笔订单就可能没有对应通知。
如果先发消息,数据库后来回滚,消费者又可能处理一笔不存在的订单。单独把两个操作换顺序,不能消灭这个间隙。
Outbox 的一种做法,是在同一个本地数据库事务里保存业务记录和待发送事件。事务提交以后,独立投递过程扫描或订阅这些事件,发送并处理确认。
这样先解决“业务与发送意图一起保存”。投递成功但状态标记失败时,仍然可能重发,所以消费者幂等没有因此省掉。Outbox 也需要监控积压、重试和清理,而不是新增一张表就算完成。

Broker 的可靠性,要明确故障范围
durable 队列、persistent 消息和发布确认,各自承担不同职责。使用哪种复制队列、能承受几个节点故障、是否配置正确,也会影响结果。
RabbitMQ 可靠性指南讨论了确认、重投与重复的关系。具体保障受队列类型与部署条件约束,不能把“持久化”解释成机房毁掉也绝不丢。
上线前应验证:节点故障以后消息是否还在,发送失败是否可追踪,未路由消息有没有告警,以及堆积时是否还能恢复。

消费者为什么通常完成以后才确认?
假设消费者先 ACK,再修改数据库。随后修改失败,Broker 已经认为这条消息处理过,就不会因为这次故障自动重投。
把确认放在业务可靠完成以后,更有利于失败重投。但如果数据库已经提交,消费者在 ACK 前崩溃,消息就可能再次到达。
所以,后确认解决不了重复。消费者可以用业务事件 ID,在本地事务中检查并记录已处理状态,使同一个事件不会重复产生同一种数据库结果。
发邮件、调用支付等外部动作还要看对方是否支持幂等键、如何查询结果和怎样补偿。不能把本地去重表当成所有外部副作用的原子保证。
重试没有终点,可靠性也会变成负担
暂时性网络错误可以重试,参数永久错误则可能一直失败。需要最大次数、退避、死信或人工处理,并保留失败原因。
对于有顺序要求的事件,还要说明重试会不会让后来的消息越过它。这些属于业务一致性,而不只是队列里有没有字节。
面试官继续追问
发布确认超时,就说明消息没进队列吗?
不一定。可能消息已经接收,只是确认没有回来。重发需要稳定消息 ID,后续处理要容忍重复。
所有消息队列的 ACK 都一样吗?
不同。Kafka 的生产确认、消费者位点与 RabbitMQ 的发布确认、消费确认有各自机制。可以比较职责,不能直接互换配置名。
能不能仅靠 MQ 实现业务恰好一次?
通常不能这样承诺。业务数据库和外部服务也参与最终结果。需要说明事务边界、幂等能力、故障处理及保障范围。
面试速记卡
- 交接链:业务、生产者、Broker、消费者分别检查。
- 两种确认:发布成功不等于消费业务完成。
- 双写间隙:Outbox 保存业务与发送意图,投递仍可能重复。
- 消费顺序:业务可靠完成后确认,同时做好幂等。
- 故障恢复:重试、死信、监控与补偿一起设计。
