想象你搬进一座没有门牌号的大型公寓楼,楼下只有一位守门大爷。你想收快递,得先告诉他“我是3栋2单元501室”,快递员才能把包裹精准送到你手上;否则包裹只能在门口堆成山,甚至被退回。在软件系统世界里,订单系统、支付系统、库存系统之间也需要这样一位“守门大爷”帮它们传话——它叫 RabbitMQ。 寄包裹的系统叫“生产者”,收包裹的系统叫“消费者”,大爷手里的分拣规则叫“交换器”,每个系统门口挂的信箱叫“队列”。最巧妙的是,大爷从不直接把包裹塞到消费者手里,而是先放进对应信箱,消费者什么时候方便,什么时候开箱取件。寄件人和收件人完全不用同时在线,也不会互相堵门。在秒杀大促这类“流量洪峰”场景下,RabbitMQ 就像一座大水库,先把汹涌的水流蓄起来,再按下游的承受能力缓缓放水——这就是架构师常说的“削峰、解耦、异步”。
理解 RabbitMQ 的路由模型,是搭建高可用分布式系统和 AI 服务管道的必修课。无论是异步推理、日志采集还是训练任务调度,消息队列都能让你的服务像邮局一样井然有序。
可视化演示
展示Direct/Fanout/Topic/Headers四种交换器路由方式,消息从生产者经交换器到队列的完整路径,及死信与延迟队列机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
四种交换器路由方式 Direct:routing key精确匹配 Fanout:广播到所有绑定队列 Topic:模式匹配*和#通配符 Route=f(Exchange,Key,Binding) 交换器类型决定路由策略
消息模型基础
在公寓楼的比喻里,住户最关心的问题是:信到底怎么送到我手里?答案是三个角色的接力。寄信人(生产者)把信交给守门大爷,大爷按分拣规则把信放进对应信箱(队列),住户(消费者)有空时开箱取信。这三者之间没有任何人需要实时等待对方——寄信人投完信就走,住户也不必守在信箱旁。
在 RabbitMQ 中,这条链路被抽象成 AMQP 的消息流动模型:生产者把消息发送到交换器,交换器根据路由键把消息塞进一个或多个队列,队列再推送给订阅它的消费者。注意,RabbitMQ 不是点对点直传的模型,交换器和队列之间的“配对关系”需要提前用绑定声明好,就像住户要先在邮局登记“我的信请投 3 栋 2 单元 501 室”。
一条消息从诞生到被消费,经历六个阶段:创建、发布、路由、入队、投递、确认。任何一个阶段断了,消息都不会被消费者真正拿到。理解这条链路,是理解后续所有路由规则的前提——毕竟,你连信要经过哪些中转站都不清楚,又怎么谈得上精准投递呢?
回到公寓楼:只要守门大爷、信箱、住户三者分工明确,无论楼里住了多少家系统,消息永远有条不紊。这就是消息模型基础——一切路由规则的底层骨架。
- 生产者发布P 是生产者 Producer,它只管把消息交到交换器手里,完全不知道最终谁会消费,就像寄件人只把信投进邮筒。
- 交换器路由E 是交换器 Exchange,它拿着消息的路由键,按绑定关系决定消息去哪个队列,这是整条链路的大脑。
- 队列暂存Q 是队列 Queue,它像住户信箱一样把消息暂存起来,即使消费者此刻不在线,消息也不会丢。
- 消费者处理C 是消费者 Consumer,它从队列拉取或等待推送,处理完返回确认回执,一次投递才算闭环。
交换器类型
守门大爷不是只会一种分拣方式。有的住户要求“只有写着我名字的信才投给我”,有的说“楼里所有公告我都看”,还有的说“凡是 order 开头的信都要”。大爷准备了四种分拣规则,对应 RabbitMQ 的四种交换器类型。
Direct 直连交换器是最严格的分拣员。它要求消息的路由键与绑定的键完全相等,一个字都不能差,就像挂号信要求收件人姓名与身份证完全一致。常用于按事件类型精准投递,例如订单取消事件只发给订单服务。
Fanout 广播交换器则完全相反,它无视路由键,把消息复制给所有绑定的队列,就像物业在楼门口贴公告,人人可见。它最适合群发通知、缓存刷新这类“所有系统都要知道”的场景。
Topic 主题交换器是 Direct 的升级版,它允许绑定键里出现两个通配符:* 匹配一个词,# 匹配零个或多个词。比如绑定键 order.* 能匹配 order.created,却不能匹配 order.create.done,而 order.# 两者都能匹配。它是微服务事件总线的最爱。
Headers 头式交换器更“任性”,它不看路由键,而是检查消息头部的一堆键值对是否匹配绑定声明的参数,适合按消息属性而非命名规则路由的场景。
大爷的四种分拣方式各有适用场景:重要的信走 Direct,公告走 Fanout,需要分类订阅的走 Topic,按属性筛选的走 Headers。选对交换器,等于给你的消息选对了投递策略。
- Direct 判定式绑定键 b 与路由键 r 严格相等才投递,适用于点对点精确通知。
- Fanout 恒真式不论 b 与 r 是什么都返回真,消息被复制到所有绑定队列,广播语义。
- Topic 通配式glob 函数把
*翻译成匹配一个词、#翻译成匹配任意段,再进行字符串匹配。 - Headers 头部式交换器读取消息头部键值对,与绑定时的参数做键值匹配,路由键被忽略。
消息路由机制
有了分拣规则,还得知道规则如何真正生效。在邮局里,这取决于两样东西:信封上的地址和住户登记的投递偏好。RabbitMQ 中,地址就是路由键,偏好就是绑定关系。
路由键是生产者发送消息时附带的一个字符串,比如 order.created。它本身没有任何意义,意义全靠交换器与队列之间的绑定来赋予。绑定关系由消费方声明,例如订单服务声明“队列 order_svc 绑定到 order_bus 交换器,绑定键为 order.*”。这条绑定就像住户在邮局填了张登记表:凡是 order 开头的事件的信,都投进我的信箱。
路由匹配的逻辑可以归纳为一步:交换器收到消息后,拿着消息的路由键,依次与每条绑定的绑定键做一次匹配运算(具体算法由交换器类型决定),把所有匹配成功的队列都列为投递目标。一个路由键可以匹配多个队列,一个队列也可以被多个交换器绑定,形成灵活的发布订阅拓扑。
回到公寓楼的场景:大爷手里有一本“投递偏好登记册”,每条信都要翻册子找匹配项。路由机制的精髓就是“地址 + 登记册”的联动——路由键写得再花哨,没有绑定关系也白搭。
- 绑定关系存在bind(E, qi, bi) 表示队列 qi 已经用绑定键 bi 绑定到交换器 E,这是匹配的前提。
- 路由键匹配match(bi, r) 表示本次消息的路由键 r 与绑定键 bi 匹配成功,具体算法由交换器类型决定。
- 取交集组成集合花括号收集所有同时满足“已绑定”和“匹配成功”的队列,形成最终投递目标集合。
- 集合的威力这个集合可能是空集(消息被丢弃),也可能是多个队列,这正是 Fanout 广播和 Topic 多播的数学基础。
队列与可靠性
住户最怕什么?信箱里的信被风刮走、被邻居冒领、或者自己出差回来发现信已被人扔掉。RabbitMQ 同样要回答三个“怕”:服务器重启消息会不会丢?发送时网络抖一下消息会不会没?消费者读到一半挂了消息会不会丢?答案都藏在“可靠性三板斧”里。
第一板斧是持久化。把交换器、队列、消息三者都标记为 durable,消息就会写入磁盘,像把信放进了防火保险柜,Broker 重启也丢不了。值得注意的是,只有消息本身、它所在的队列、转发它的交换器三处都持久化,才算真正上保险,缺一个都可能在重启的瞬间漏掉。
第二板斧是生产者确认。生产者开启 publisher confirm 模式后,Broker 成功落盘会回一个 ack;没收到 ack 就重发。这相当于挂号信的回执——寄件人知道信真被邮局收下了。
第三板斧是消费者确认。消费者处理完业务后手动调用 basic.ack,Broker 收到回执才删除消息;若消费者崩溃或返回 basic.nack/basic.reject,消息会重回队列投给其他消费者。配合 basic.qos 预取限制(比如一次只取 10 条),还能防止消费者被瞬间灌爆。三者的关系,可以用一个简单的概率公式表达:
保险箱(持久化)、挂号回执(生产确认)、签收单(消费确认)三件套齐了,住户才敢说“我的信稳了”。可靠性从来不是单点措施,而是一条环环相扣的信任链。
- 持久化概率P持久化 表示消息与队列落盘成功的概率,它保证了 Broker 重启后消息仍在。
- 生产确认概率P生产确认 表示生产者收到 publish ack 的概率,它保证了消息真的进了 Broker 而非丢在路上。
- 消费确认概率P消费确认 表示消费者处理成功并返回 ack 的概率,它保证了业务真正执行完毕。
- 乘法原理三重保障是“与”的关系,每一重都独立承担一道防线,相乘即得最终不丢概率。
死信队列机制
总有一些信永远送不出去:收件人搬家了、信在信箱里放太久发霉了、信箱满得塞不进去了。邮局的处理方式是把它们扔进“死信办公室”,等待人工认领。RabbitMQ 也设了这样一个办公室——死信队列(DLQ)。
什么消息会被判“死刑”?主要有三种。第一,消费者显式拒绝且要求不重回队列(basic.reject 或 basic.nack 且 requeue=false);第二,消息超过设定的存活时间 TTL 未被消费;第三,队列达到最大长度或容量溢出,最早的消息被挤出去。这三种情况分别对应“收件人拒收”“信件超期”“邮箱爆满”。
实现死信机制不需要特殊队列类型,只需要给普通队列加两个参数:x-dead-letter-exchange(死信交换器)和 x-dead-letter-routing-key(死信路由键)。消息死后会被自动转发到指定死信交换器,再按路由键进入死信队列,由专门的补偿程序重新处理或记录告警。
死信办公室不是终点,而是“人工服务台”:补偿程序定期扫描死信队列,分析失败原因、修正数据、重新投递。有了它,消息的“意外死亡”也能被记录、被追溯、被拯救。
- 拒绝条件reject(m) 表示消费者明确拒收且不重回队列,消息进入死信通道。
- 过期条件expire(m) 表示消息在队列中存活超过 TTL 设定的时长,被判为“超时未取”。
- 溢出条件overflow(m) 表示队列容量已满,维护了最久或最新的消息被挤出队列。
- 逻辑或汇总三个条件用“或”连接,只要命中其一,消息就被送入死信队列等待兜底处理。
延迟队列实现
有时候我们不想立刻投递一封信,而是希望“30 分钟后送达”或“明天早上再提醒”。邮局的“延迟投递服务”对应 RabbitMQ 的延迟队列。虽然 RabbitMQ 原生没有延迟队列类型,但用两个现成零件就能组装出来:TTL 消息过期 + 死信交换器。
组装思路很巧妙:把消息先发到一个没有消费者订阅的“中转队列”,给它设 TTL(比如 30 秒)。消息在中转队列里安静地躺着,一旦到期,立刻被当作“死信”转发到死信交换器,再由死信交换器路由到真正的业务队列,此刻消费者才收到它。这个“死信”不是失败,而是延迟到期的闹钟响起。
用公式来理解,消息在中转队列的剩余存活时间是 TTL 减去已等待时长;当剩余时间归零,消息就触发转移。开发者因此能精确控制“消息多久后可见”,实现订单超时自动关闭、定时提醒、重试间隔控制等场景。此外,社区还提供了 rabbitmq_delayed_message_exchange 插件,用一条声明直接支持延迟消息,原理基本相同,但使用更顺手。
这就像邮局把信锁进一个定时开启的柜子,时间一到柜门自动打开,信滑进正常投递流程。延迟队列不是魔法,只是 TTL 与死信机制的一次巧妙合谋。
- 设置定时器TTL 是消息在中转队列的最大存活时长,相当于给信件设了个倒计时闹钟。
- 计算已等待t现在 − t入队 表示消息已经在中转队列里待了多久,时间在流逝。
- 求剩余时间TTL 减去已等待时长,得到剩余存活时间,它随等待逐渐逼近零。
- 到期触发转移当剩余时间小于等于零,消息被判定过期,经死信交换器路由到真正业务队列。
AMQP协议设计
为什么全世界的“邮局”能互通?因为有一套通用的国际邮政公约。RabbitMQ 的跨语言、跨平台能力同样来自一个公开标准——AMQP(高级消息队列协议)。它把邮局内部的分工写成了协议条款,任何语言写的程序只要遵守条款,就能接入同一个 RabbitMQ。
AMQP 协议把消息系统划分为清晰的层次:客户端应用通过 TCP 建立一条连接(Connection),连接内部再创建多个轻量的信道(Channel)。信道是真正干活的人——发消息、消费、确认都在信道上进行。一条连接可以复用出几十条信道,就像一辆邮车一次能装几百个分格信箱,不必为每封信单独雇一辆车。
协议还定义了虚拟主机(vhost)的概念:一个 RabbitMQ 服务可以划分多个相互隔离的 vhost,每个 vhost 有自己独立的交换器、队列、绑定关系,就像同一栋大楼里有多家邮局分局,互不串门。默认的 vhost 是 /,生产环境通常按业务线或环境拆分成多个 vhost 做租户隔离。
AMQP 就像邮局的《万国邮政公约》,让不同语言、不同团队写的系统能够用同一种“语言”通信。理解连接、信道、vhost 的层次,你就能在复杂的微服务网络中,准确找到属于自己的那家“分局”。
- 交换器集合{E1,…,En} 表示某个 vhost 内声明的一批交换器,它们是分拣规则的集合。
- 队列集合{Q1,…,Qm} 表示该 vhost 内的队列集合,即所有住户信箱。
- 绑定集合绑定关系集合把交换器与队列两两连接起来,形成路由拓扑。
- 并集语义三者的并集构成一个 vhost 的全部资源,它们是逻辑隔离的独立小宇宙。
AI 应用场景
AI 推理服务的请求削峰
阶段:模型推理服务层
解决的问题:AI 服务常被突发请求打垮,比如营销活动瞬间涌入大量图片识别请求,GPU 推理服务直接超时。传统同步调用下,每来一个请求就占一个连接,服务端被压垮时客户端只能反复失败重试。用 RabbitMQ 做请求队列后,所有推理请求先进入队列“蓄水”,推理 worker 按自身吞吐量从容消费,系统从“被流量推着走”变成“按节奏干活”。
具体案例:某内容平台上线 AI 审核功能,接入 RabbitMQ 的 Fanout 交换器把图片审核事件广播给审核服务和日志服务,审核 worker 集群自动扩缩容消费队列,峰值 QPS 从 500 平缓到 8000 而服务零宕机。
多模型版本灰度路由
阶段:模型部署与流量分配
解决的问题:新模型上线要灰度验证,传统做法是改代码切流量,风险高且回滚慢。利用 Topic 交换器的通配路由,按模型版本设计路由键,例如 model_v2.predict 的消息路由到新模型队列,model_v1.predict 走旧模型队列,实现一键灰度、按比例切换。
具体案例:推荐系统团队把 CTR 预估模型升级到 v2,通过绑定键 model.v2.* 把 10% 线上请求路由到新模型,剩余 90% 留在 v1。观察一周指标后,把绑定比例调到 100%,全程无代码发布。
失败推理任务的死信重试
阶段:任务调度与异常恢复
解决的问题:分布式训练或推理任务时常因数据源抖动、依赖服务超时而失败,直接丢弃任务会污染训练样本。把失败任务作为死信转入 DLQ,配合补偿程序分析失败原因、修正数据后重新投递,形成“失败—诊断—重试”闭环。
具体案例:某语音识别团队用延迟队列 + 死信队列实现 task 重试:任务消费失败后 reject 进死信队列,补偿程序按指数退避(1s、4s、16s)把任务重新投递回工作队列,三次仍失败则告警人工介入。
Python 演示
import re class Broker: """RabbitMQ 路由核心逻辑模拟器""" def __init__(self): self.exchanges = {} self.queues = {} def declare_exchange(self, name, etype): self.exchanges[name] = {'type': etype, 'bindings': []} def declare_queue(self, name): self.queues[name] = [] def bind(self, exchange, queue, key): self.exchanges[exchange]['bindings'].append((queue, key)) @staticmethod def _match(etype, binding_key, routing_key): if etype == 'fanout': return True if etype == 'direct': return binding_key == routing_key if etype == 'topic': pattern = binding_key.replace('.', r'\.').replace('#', '.*').replace('*', '[^.]+') return re.match('^' + pattern + '$', routing_key) is not None return False def publish(self, exchange, routing_key, message): targets = [] for queue, key in self.exchanges[exchange]['bindings']: if self._match(self.exchanges[exchange]['type'], key, routing_key): self.queues[queue].append(message + ':' + routing_key) targets.append(queue) return targets if __name__ == '__main__': mq = Broker() mq.declare_exchange('order_bus', 'topic') mq.declare_exchange('notice', 'fanout') mq.declare_queue('order_svc') mq.declare_queue('log_svc') mq.declare_queue('sms_svc') mq.bind('order_bus', 'order_svc', 'order.*') mq.bind('order_bus', 'log_svc', 'system.#') mq.bind('notice', 'sms_svc', '') print(mq.publish('order_bus', 'order.created', '订单已创建')) print(mq.publish('order_bus', 'system.warn', '磁盘告警')) print(mq.publish('notice', 'any', '全场促销'))
self.exchanges[name] = {'type': etype, 'bindings': []}每个交换器登记自己的类型和绑定清单,相当于邮局登记本。def _match(etype, binding_key, routing_key)把三种交换器的匹配策略收敛到一个函数,是路由模拟的核心。pattern = binding_key.replace('.', r'\.').replace('#', '.*').replace('*', '[^.]+')将 Topic 的*与#通配符翻译成正则表达式,实现模糊匹配。mq.bind('order_bus', 'order_svc', 'order.*')声明队列 order_svc 用绑定键order.*挂到 order_bus 交换器下。mq.publish('order_bus', 'order.created', '订单已创建')发布一条路由键为order.created的消息,返回它投递到的队列列表。
常见误区
误区我以为 RabbitMQ 就是生产者把消息直接塞进队列,然后消费者从队列取消息,交换器是什么东西?完全没必要吧。
正解消息必须先进交换器,由交换器按绑定关系路由到队列。就像寄信必须经过邮局分拣,你不可能把信直接塞进别人家的信箱——你根本不知道信箱在哪。交换器是整个路由模型的中枢,跳过它就等于放弃 RabbitMQ 最核心的灵活路由能力。
为何常见:很多人先学了 Kafka 的 topic 直写模型,再接触 RabbitMQ 时容易套用旧经验,忽略了 AMQP 独有的“交换器—绑定—队列”三层设计。
误区我觉得 Fanout 交换器也应该能传路由键做筛选,比如只把消息发给绑定键匹配的队列,这样更灵活。
正解Fanout 的全部语义就是“无视一切键,复制给所有绑定队列”。它适合广播场景,如全系统配置刷新、公告通知。想筛选就别用 Fanout,请选 Direct 或 Topic。就像楼里的广播大喇叭,不会因为你在睡觉就不放给你听。
为何常见:初学 RabbitMQ 时比较容易把四种交换器混为一谈,以为 Fanout 只是“广播版的 Direct”,忽略了它根本不比较路由键的设计初衷。
误区我认为 Topic 里 `*` 和 `#` 都一样,都是匹配一个单词,顶多 `#` 匹配得更宽松一点。
正解* 只能匹配一个由点分隔的词,而 # 匹配零个或多个词。例如绑定键 order.# 既能匹配 order.created,也能匹配 order.created.done 甚至 order;而 order.* 只能匹配 order.created。两者差异在多层路由键下尤其关键。
为何常见:大家都很熟悉正则表达式里的 * 是“任意多个字符”,于是把这种直觉错误地迁移到 RabbitMQ 的通配符上,造成路由配置偏差。
误区只要生产者把消息发出去并且看到它进了队列,我觉得这条消息就绝对安全了,消费者早晚会消费它。
正解消息进入内存队列不等于落盘,消费者默认 auto_ack 会在“拿到消息”而非“处理成功”时就确认。若消费者处理到一半崩溃,或者 Broker 重启且未持久化,消息照样丢。必须组合使用 durable 持久化、publisher confirm、手动 ack 三件套,才能做到真正不丢。
为何常见:本地用 RabbitMQ 做演示时一切顺利,容易让人误以为“发送成功 = 消费可靠”,只有在生产环境的宕机和重试中才会暴露问题。
误区我以为死信队列和延迟队列是 RabbitMQ 内置的特殊队列类型,用某种神奇的声明就能直接创建出来。
正解RabbitMQ 只有普通队列类型。死信队列是通过给普通队列设置 x-dead-letter-exchange 参数,把异常消息转发到另一个普通队列实现的;延迟队列则是“TTL + 死信交换器”的组合,或使用官方延迟插件。它们都是配置出来的模式,不是新类型。
为何常见:市面上文章常把“死信队列”“延迟队列”当作独立概念讲解,容易让人误以为它们和 Fanout、Topic 一样是原生类型,忽略其底层组合本质。
面试题
Q1. RabbitMQ 中生产者发送的消息首先被送达哪个组件?
- A.目标队列里暂存
- B.交换器进行路由← 正确答案
- C.消费者直接处理
- D.死信队列待处理
答案:B。RabbitMQ 的 AMQP 模型中,消息必须先到达交换器,由交换器根据绑定关系和路由键决定投递到哪个队列,而不是直接进队列。就像信必须先到邮局分拣台,再由分拣员决定投进哪个信箱。
Q2. 希望把一条消息广播给所有绑定队列,应选用哪种交换器?
- A.Direct 直连交换器
- B.Fanout 广播交换器← 正确答案
- C.Topic 主题交换器
- D.Headers 头式交换器
答案:B。Fanout 交换器无视路由键,把消息复制给所有绑定的队列,是标准的广播语义。Direct 需要键完全相等,Topic 需要通配匹配,Headers 需要头部匹配,都不具备“来者不拒”的广播特性。
Q3. Topic 交换器绑定键为 order.*,以下哪个路由键可以匹配?
- A.order.created← 正确答案
- B.order.pay.done
- C.user.order.paid
- D.created.order
答案:A。* 只能匹配一个由点分隔的词,order.* 能匹配 order.created(一个词),但不能匹配 order.pay.done(两个词)。B 需要 order.# 才能匹配,C 和 D 的前缀与绑定键不一致。
Q4. 消费者处理完消息返回 basic.ack 后,该消息的状态是?
- A.标记删除且移出队列← 正确答案
- B.重新投递给其他消费者
- C.转入死信队列等待
- D.留在队列等待重试
答案:A。basic.ack 是消费者给 Broker 的“处理成功”回执,Broker 收到后才会把消息从队列中删除。若消费者进程崩溃未回执,消息才会被重新投递;拒收且不重回队列才会进死信队列。
Q5. 以下哪些情况会使消息进入死信队列?(多选)
- A.消费者拒绝且不重回队列← 正确答案
- B.消息超时存活期过期← 正确答案
- C.队列已满触发丢弃溢出← 正确答案
- D.消费者处理成功确认
答案:ABC。死信的三大来源是:reject/nack 且 requeue=false(拒收)、消息超过 TTL(超期)、队列长度或容量溢出。D 是正常消费路径,消息应被删除而非进入死信队列。
Q6. Direct 与 Topic 交换器的根本区别是什么?
- A.前者精确匹配后者通配匹配← 正确答案
- B.前者广播投递后者单播
- C.前者按消息体路由后者按键
- D.前者持久化后者不持久化
答案:A。Direct 要求路由键与绑定键完全相等,Topic 则支持 * 和 # 通配符做模糊匹配,这是两者最本质的差别。广播是 Fanout 的职责;两者都按路由键路由;持久化由队列参数决定,与交换器类型无关。
Q7. 要保证消息不丢失,以下哪些措施是必要的?(多选)
- A.队列与消息开启持久化← 正确答案
- B.生产者开启发布确认← 正确答案
- C.消费者采用手动确认← 正确答案
- D.消息发送前先断开连接
答案:ABC。可靠性三板斧是:持久化(防重启丢失)、publisher confirm(防发送丢失)、手动 ack(防消费丢失),三者缺一不可。先断开连接只会导致消息根本发不出去。
Q8. 路由键与绑定键完全相等时,哪个交换器必定投递?
- A.Direct 直连交换器← 正确答案
- B.Fanout 广播交换器
- C.Topic 主题交换器
- D.Headers 头式交换器
答案:A。Direct 的匹配规则就是“路由键 == 绑定键”,键值相等必然投递。Fanout 虽必投递但与键无关,Topic 需满足通配规则,Headers 根本不看路由键而看消息头。
Q9. 消费速度跟不上导致消息大量积压时,常用解法有哪些?(多选)
- A.横向增加消费者实例← 正确答案
- B.拆分子队列多消费者并行消费← 正确答案
- C.把消息合并成批量处理← 正确答案
- D.直接清空队列丢弃消息
答案:ABC。积压的根本矛盾是“生产速度 > 消费速度”,解法围绕提升消费吞吐:加消费者实例、拆分队列并行处理、批量消费减少 IO 开销。丢弃数据会破坏业务完整性,是不可接受的兜底手段。
Q10. Headers 交换器根据什么信息匹配路由?
- A.消息头部键值对← 正确答案
- B.消息体的大小
- C.路由键的前缀
- D.队列的长度排行
答案:A。Headers 交换器完全忽略路由键,改为检查消息头部声明的键值对是否匹配绑定参数(配合 x-match 的 all/any 语义)。这是它与其他三种交换器最根本的区别,适合按元数据而非命名规则路由的场景。