← 返回题目列表

AMQP 是什么?Exchange、Queue、Binding 和 Routing Key 如何协作?

高频 中等 第 3 / 27 题 更新于 2026/07/31
AMQPRabbitMQExchangeQueueRouting Key

简化版

AMQP 是面向消息中间件的应用层协议,典型实现是 RabbitMQ。生产者不直接把消息发给队列,而是发给 Exchange;Exchange 根据类型、Binding 和 Routing Key 把消息路由到一个或多个 Queue;消费者从 Queue 拉取或接收消息。面试重点是四种交换机、ACK、持久化、死信和延迟重试。

详细版

AMQP 的关键组件是:

组件作用
Producer发送消息
Exchange接收消息并按规则路由
Queue存储待消费消息
Binding连接 Exchange 和 Queue 的路由规则
Consumer消费队列消息

路由模型可以这样理解:

Producer -> Exchange --Binding/RoutingKey--> Queue -> Consumer

常见 Exchange 类型包括 direct、fanout、topic、headers。direct 按 Routing Key 精确匹配,fanout 广播到绑定队列,topic 支持通配符匹配,headers 根据消息头匹配。可靠性上要关注生产者确认、消息持久化、消费者手动 ACK、重试和死信队列。

完整版教学

一、AMQP 为什么不让生产者直接写队列

如果生产者直接写队列,发送方就必须知道具体队列名,路由逻辑会散落在各个业务服务里。AMQP 引入 Exchange,是为了把“发消息”和“消息应该去哪里”解耦。生产者只关心发到哪个交换机和带什么 Routing Key,具体投递到哪些队列由 Binding 决定。

这个设计让拓展更容易。例如原来订单创建消息只给库存服务,后来风控服务也要订阅订单创建事件,只需要新建一个队列并绑定到同一个 Exchange,不必修改订单服务。

order-service
    |
    v
order.exchange
    | binding: order.created
    +--> inventory.queue
    +--> risk.queue

二、Exchange 类型决定路由规则

AMQP 的 Exchange 像“分拣中心”,不同类型代表不同分拣规则。direct 适合精确路由,fanout 适合广播,topic 适合按主题层级匹配,headers 较少用,因为它依赖消息头匹配,性能和可读性通常不如 Routing Key。

类型匹配方式例子
directRouting Key 完全相等order.created
fanout忽略 Routing Key,广播系统公告
topic* 匹配一段,# 匹配多段order.*pay.#
headers根据消息头键值匹配format=pdf

面试里常问 topic 的通配符:* 只匹配一个单词,# 可以匹配零个或多个单词。例如 order.* 能匹配 order.created,不能匹配 order.created.usorder.# 可以匹配后者。

三、Binding 和 Routing Key 是如何配合的

Binding 是 Exchange 到 Queue 的规则,Routing Key 是消息携带的路由键。direct 交换机下,消息 Routing Key 与 Binding Key 相同才投递;topic 交换机下,Binding Key 可以带通配符。

Exchange: order.topic
Binding A: order.created.*  -> queue_a
Binding B: order.#          -> queue_b

Message routing_key = order.created.vip

结果:
queue_a 收到
queue_b 收到

同一条消息可以进入多个队列,所以 AMQP 能自然支持发布订阅。但同一个队列里的多个消费者通常是竞争消费,一条消息只会被其中一个消费者处理。

四、ACK 机制解决消费者失败问题

消费者拿到消息后,如果业务处理到一半宕机,队列不能直接把消息删掉。AMQP 通过 ACK 机制解决这个问题:消费者处理成功后发送 ACK,Broker 才删除消息;如果连接断开或 NACK,消息可以重新入队或进入死信。

Queue -> Consumer
         |
         +-- 处理成功 -> ACK -> 删除
         |
         +-- 处理失败 -> NACK/requeue 或进入 DLX

实际项目里通常使用手动 ACK。自动 ACK 风险很大,因为消息一推给消费者就被认为成功,业务处理失败也可能丢消息。

五、消息可靠性需要三段都可靠

消息从生产到消费至少经过三段:生产者到 Broker、Broker 存储、Broker 到消费者。任何一段都可能丢。可靠消息不是只开一个持久化开关就完事,而是要组合多个机制。

阶段风险常用机制
生产者 -> Broker网络失败、路由失败publisher confirm、mandatory return
Broker 存储Broker 宕机durable queue、persistent message、镜像/仲裁队列
Broker -> Consumer消费失败manual ACK、NACK、死信队列

例如队列 durable 但消息不是 persistent,Broker 重启后消息仍可能丢;消息 persistent 但队列不是 durable,队列没了也保不住消息。

RabbitMQ 可靠性要按链路记:发送确认、队列和消息持久化、消费手动 ACK,缺一段就有缺口。

六、死信队列如何承接失败消息

死信队列不是特殊队列,而是普通队列加上死信交换机配置。当消息被拒绝、过期、队列满,或者达到重试上限时,可以转发到 DLX,再进入死信队列。这样失败消息不会无限阻塞主队列,也不会悄悄丢掉。

main.queue --失败/过期/拒绝--> dlx.exchange --> dead.queue

例如订单通知最多重试 5 次,每次失败后延迟一段时间再投回主队列,超过 5 次进入死信队列并告警。死信队列让系统具备“可观察、可补偿”的能力。

七、预取和背压决定消费稳定性

消费者如果一次被推太多消息,处理不过来会导致内存上升、超时、重试风暴。AMQP 里的 prefetch 可以限制未 ACK 消息数量。例如 prefetch=50 表示一个消费者最多同时持有 50 条未确认消息。

假设单条消息平均处理 100 ms,一个消费者并发处理 10 条,理论吞吐约 10 / 0.1 = 100 条/s。如果 Broker 一次推 5000 条到这个消费者,绝大部分只是在内存里排队,没有提高吞吐,反而增加失败恢复成本。

合理 prefetch = 消费者并发能力 * 单任务缓冲系数
例如 10 * 2 = 20,而不是无限推送

八、常见误区与追问

  • 误区:RabbitMQ 的生产者直接把消息发给 Queue。 AMQP 模型里生产者发给 Exchange,由 Exchange 根据 Binding 路由到 Queue。
  • 误区:队列 durable 就能保证消息不丢。 队列持久化、消息持久化、生产者确认和消费者 ACK 都要配合。
  • 误区:fanout 比 topic 更高级。 它们只是路由规则不同,fanout 适合广播,topic 适合按主题匹配。
  • 误区:自动 ACK 更省事也更可靠。 自动 ACK 会在业务处理前确认消息,消费者失败时可能丢消息。
  • 追问:direct 和 topic 的区别是什么? direct 精确匹配 Routing Key,topic 支持 *# 通配。
  • 追问:死信队列有什么用? 承接失败、过期、被拒绝的消息,便于告警、排查和人工补偿。
  • 追问:如何避免消费者被打爆? 设置 prefetch、控制消费并发、失败退避重试,并监控堆积量和消费延迟。

九、加强记忆

AMQP 可以按“生产者发 Exchange,Exchange 按 Binding 和 Routing Key 投 Queue,消费者 ACK 后删除”来记。Exchange 有 direct、fanout、topic、headers 四类,可靠性看生产确认、持久化、手动 ACK 和死信队列,稳定性看 prefetch、重试退避和堆积监控。把路由模型和可靠链路画出来,面试回答就很清楚。