Sunday 的面试指南

消息队列如何保证消息不丢失?为什么收到 ACK 还不一定代表业务完成?

🧑‍💻 面试官:怎样避免消息丢失?

🙋‍♂️ 我:生产者等确认,队列做持久化,消费者处理完再 ACK。

🧑‍💻 面试官:生产者已经收到 ACK,订单邮件是不是已经发出去了?

🙋‍♂️ 我:不是,那是 Broker 的发布确认,不是消费者完成业务。

🧑‍💻 面试官:如果数据库提交成功,程序却在发送消息前崩溃呢?如果确认只是丢在网络上,重发会不会出现两条消息?

消息可靠性要沿着「整条交接链」来看。某一段确认成功,只能说明这一段满足了对应条件,不能替后面的业务作保证。

面试速答(60 秒版)

消息是否可靠,需要分别检查业务到生产者、生产者到 Broker、Broker 存储与复制,以及消费者处理这几段。

生产者要处理发送失败和确认不确定,Broker 要按产品机制配置持久化、复制与故障恢复。消费者则应在业务处理可靠完成后确认,失败按规则重试或进入死信处理。

发布确认与消费确认不是同一个 ACK。收到发布确认,不代表消费者已经执行;数据库提交以后再发消息,还存在双写间隙,可以考虑本地事务写 Outbox,再可靠投递。

同时,确认丢失和消费者崩溃都可能带来重复消息。系统需要业务幂等、可追踪状态与补偿机制,不能承诺“打开持久化就端到端恰好一次”。

发布确认,不等于业务完成

知识点详解:消息在哪些地方可能掉下去?

发布确认,只是第一次交接

生产者调用发送 API,不等于 Broker 已经接住消息。操作可能还在客户端缓冲,也可能因网络中断留下未知结果。

以 RabbitMQ 为例,publisher confirms 用于发布者确认;consumer acknowledgements 用于消费者确认。官方确认文档明确区分了两者。

还需要确认消息是否成功路由。某些情况下,无法路由的消息也可能得到发布确认,需要结合 mandatory 返回等对应机制处理,不能只看“确认成功”就认为指定队列里一定有消息。

对于持久消息与可靠队列,确认时机和复制要求也有具体约定,应按选用的队列类型检查,而不是所有产品都背同一套词。

最容易漏掉的,是数据库与消息之间

假设业务要保存订单,再通知库存服务。

如果先提交数据库,接着进程崩溃,消息还没发送,这笔订单就可能没有对应通知。

如果先发消息,数据库后来回滚,消费者又可能处理一笔不存在的订单。单独把两个操作换顺序,不能消灭这个间隙。

Outbox 的一种做法,是在同一个本地数据库事务里保存业务记录和待发送事件。事务提交以后,独立投递过程扫描或订阅这些事件,发送并处理确认。

这样先解决“业务与发送意图一起保存”。投递成功但状态标记失败时,仍然可能重发,所以消费者幂等没有因此省掉。Outbox 也需要监控积压、重试和清理,而不是新增一张表就算完成。

Outbox:先保存发送意图

Broker 的可靠性,要明确故障范围

durable 队列、persistent 消息和发布确认,各自承担不同职责。使用哪种复制队列、能承受几个节点故障、是否配置正确,也会影响结果。

RabbitMQ 可靠性指南讨论了确认、重投与重复的关系。具体保障受队列类型与部署条件约束,不能把“持久化”解释成机房毁掉也绝不丢。

上线前应验证:节点故障以后消息是否还在,发送失败是否可追踪,未路由消息有没有告警,以及堆积时是否还能恢复。

确认超时,也可能已经接收

消费者为什么通常完成以后才确认?

假设消费者先 ACK,再修改数据库。随后修改失败,Broker 已经认为这条消息处理过,就不会因为这次故障自动重投。

把确认放在业务可靠完成以后,更有利于失败重投。但如果数据库已经提交,消费者在 ACK 前崩溃,消息就可能再次到达。

所以,后确认解决不了重复。消费者可以用业务事件 ID,在本地事务中检查并记录已处理状态,使同一个事件不会重复产生同一种数据库结果。

发邮件、调用支付等外部动作还要看对方是否支持幂等键、如何查询结果和怎样补偿。不能把本地去重表当成所有外部副作用的原子保证。

重试没有终点,可靠性也会变成负担

暂时性网络错误可以重试,参数永久错误则可能一直失败。需要最大次数、退避、死信或人工处理,并保留失败原因。

对于有顺序要求的事件,还要说明重试会不会让后来的消息越过它。这些属于业务一致性,而不只是队列里有没有字节。

面试官继续追问

发布确认超时,就说明消息没进队列吗?

不一定。可能消息已经接收,只是确认没有回来。重发需要稳定消息 ID,后续处理要容忍重复。

所有消息队列的 ACK 都一样吗?

不同。Kafka 的生产确认、消费者位点与 RabbitMQ 的发布确认、消费确认有各自机制。可以比较职责,不能直接互换配置名。

能不能仅靠 MQ 实现业务恰好一次?

通常不能这样承诺。业务数据库和外部服务也参与最终结果。需要说明事务边界、幂等能力、故障处理及保障范围。

面试速记卡

  • 交接链:业务、生产者、Broker、消费者分别检查。
  • 两种确认:发布成功不等于消费业务完成。
  • 双写间隙:Outbox 保存业务与发送意图,投递仍可能重复。
  • 消费顺序:业务可靠完成后确认,同时做好幂等。
  • 故障恢复:重试、死信、监控与补偿一起设计。
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历