Files
autumn-recruitment/04.MQ/rabbitmq/推拉结合消费模式_test.md

154 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [test/review, mq, rabbitmq, pull-model, qos, prefetch, backpressure, consumer-balance]
create time: 2026-08-09 12:00
---
# 推拉结合消费模式_测试题
## 概述
本测试覆盖 RabbitMQ 消费模型的本质(Push vs Pull 混合理解)、QoS 预取策略、背压处理和消费者负载均衡。共 10 道题:6 道选择题、3 道填空题、1 道简答题。
---
## 一、选择题(6道,由浅入深)
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
### Q1(基础)— 考察定义层面
RabbitMQ Consumer API 名为 `basic.consume`(拉取)但实际运行模型是 Server Push(服务端推送)。以下说法正确的是:
A. `basic.consume` 和 `basic.get` 的行为完全相同
B. `basic.consume` 建立订阅后 Broker 持续主动投递,`basic.get` 每次调用阻塞获取单条消息
C. `basic.consume` 只能获取一次就断开连接
D. `basic.get` 是默认的生产者发送方法
### Q2(基础)— 考察行为判断
设置 `prefetch_count = 1` 可以实现公平分发(Fair Dispatch),其核心原因是:
A. Broker 会随机选择消费者分配消息
B. A 每处理完一条并发 ack 后才收到下一条,自然形成速度匹配
C. prefetch=1 时所有消息排队等候直到第一个消费者空闲
D. prefetch=1 强制只有一个消费者活跃
### Q3(进阶)— 考察核心原理
`global=true` 是 Prefetch 设置中的一个遗留参数,以下哪一项描述了在多 Channel 场景下使用 global=true 可能导致的意外行为?
A. prefetch 只对单个 Channel 生效,不会跨 Channel 共享配额
B. prefetch 对整个连接(而非单个 Channel)生效,导致一个 Channel 消耗了所有配额时其他 Channel 无法获得新消息
C. global=true 会使 prefetch_count 失效
D. global=true 只在 Fanout Exchange 中有效
### Q4(进阶)— 考察对比辨析
以下哪种预取值范围最适合通用生产环境?
A. prefetch=1
B. prefetch=0
C. prefetch=10~50
D. prefetch=10000+
### Q5(深入)— 考察场景推理
一个日终跑批系统需要从数据库全量读取商品信息并通过 MQ 推送到缓存集群预热。数据量大但每条消息处理简单。同时另一个即时订单状态推送系统要求低延迟。这两个场景分别适合的 prefetch 值是:
A. 两者都用 prefetch=1
B. 跑批用 prefetch=200,订单推送用 prefetch=3
C. 跑批用 prefetch=3,订单推送用 prefetch=200
D. 两者都用 prefetch=0(无限)
### Q6(深入)— 考察源码级别细节
在 `basic.consume` 建立的流控窗口机制中,Window 耗尽时 Broker 会发生什么?
A. Broker 立即关闭 TCP 连接
B. Broker 暂停向该 Channel 投递直到窗口回收(ack 释放槽位)
C. Broker 将多余消息转移到其他 Channel
D. Broker 丢弃超出的消息
---
## 二、填空题(3道)
### F1 — 填空
设 prefetch = N 时,Broker 可以在没有收到 ack 的情况下最多向该 Channel 发送 ______ 条消息累积在未确认状态。当 ack 一条消息后 unacked 数减一,Broker 再补发一条。
> **提示**: 这就是 prefetch 值本身的含义。
### F2 — 填空
同一个 Queue 可以有多个消费者构成 Consumer Group,RabbitMQ 以 ______ 方式将消息均匀分配给各消费者。但在 prefetch=1 时可能出现只有一个消费者活跃而另一个空等的情况。
> **提示**: 这种分发方式的英文术语是什么?
### F3 — 填空
根据调优黄金法则:先设 prefetch=1 观察各消费者的处理耗时分布,再根据 p99 耗时最高的那个消费者来估算合适的 batch size,初始设为预估值的 ______ 倍即可。
> **提示**: 回顾文档中的调优经验。
---
## 三、简答题(1道)
### S1
你正在为一家在线教育公司设计课程点播系统的消息处理 pipeline。系统有以下特征:
- 用户观看视频后会触发埋点事件(播放进度、点赞、评论),每秒约 5000 条
- 埋点数据处理流程:去重 → 聚合 → 写入时序数据库
- 去重操作涉及 Redis 分布式锁,耗时约 50ms
- 聚合操作相对较快(约 5ms)
- 写入时序数据库是 IO 密集型操作(约 20ms)
- 高峰期总处理耗时约 75ms/条消息
- 当前部署了 5 个消费者实例
请设计 QoS 配置方案并回答:
1. 整体 prefetch 值应该设置多少?为什么?
2. 是否需要对不同阶段的处理进行拆分(多队列串联)?
3. prefetch 过大或过小各自会带来什么问题?
> **答题框架提示**:
1. 基于单条消息处理耗时计算合适的 prefetch
2. 考虑是否需要将耗时长的阶段拆出独立队列
3. 大 prefetch 和小 prefetch 的权衡分析
---
## 参考答案与解析
### 选择题答案
| 题号 | 正确答案 | 解析 |
|------|---------|------|
| Q1 | B | `basic.consume` 建立订阅后 Broker 持续主动投递(Push),适用于大多数场景。`basic.get` 每次调用阻塞获取单条消息(Pull),适用于管理界面和健康检查等非实时场景。API 名为 consume 但实质是 Push,源于 AMQP 协议早期约定。 |
| Q2 | B | prefetch=1 实现 Fair Dispatch 的核心机制:A 每处理完一条、发一个 ack,才会收到下一条,自然形成速度匹配。如果 prefetch=N(大数值),Broker 可能在短时间内把 N 条消息全发给 A 而 B 空闲等待。 |
| Q3 | B | global=true 是对整个连接(而非单个 Channel)生效,在多 Channel 场景下会导致意外行为。现代客户端中一律设为 false,作用于单个 Channel。这是遗留参数,不建议使用。 |
| Q4 | C | prefetch=10~50 是通用生产环境默认值。prefetch=1 吞吐量较低适合处理耗时差异大的场景;prefetch=100~500 吞吐最高但内存压力大;prefetch=0(不设置)为无限值严禁在生产中使用。 |
| Q5 | B | 跑批场景:数据量大但处理简单,适合高吞吐配置 prefetch=200 配合批量更新 Redis pipeline。订单推送:latency 优先级高于 throughput,设置 prefetch=3 让消费者尽快处理完再拿下一条。 |
| Q6 | B | 流控窗口耗尽时,Broker 暂停向该 Channel 投递直到窗口回收。这是内建的背压机制——消费者发送 basic.ack 后窗口恢复,或者 window refill 后继续投递。不会丢消息也不会断连。 |
### 填空题答案
| 题号 | 答案 | 解析 |
|------|------|------|
| F1 | N | prefetch_count 定义了单个 Channel 上允许的最大未确认消息数。这 N 条消息累积在 unacked 状态,ack 一条后 Broker 再补发一条。prefetch 越大意味着降低网络 RTT 开销但也增加了内存压力和崩溃损失量。 |
| F2 | Round-Robin(轮询) | RabbitMQ 以轮询方式将消息平均分配给同一 Queue 的各消费者。但 Round-Robin 不一定是公平的——处理快的消费者实际上承担更多工作量,这正是 prefetch=1 要解决的问题。 |
| F3 | 2~3 | 调优黄金法则:先设 prefetch=1 观察各消费者的处理耗时分布,再根据 p99 耗时最高的消费者估算合适的 batch size,初始设为预估值的 2~3 倍。之后通过监控 unacked 数量和 queue depth 微调。 |
### 简答题参考答案
S1:**参考答案要点**:
1. 单条消息总处理耗时约 75ms,5 个消费者实例理论上可支撑 5 / 75ms ≈ 66 QPS。但实际需求为 5000 QPS,远超出单节点处理能力。prefetch 建议设置为 10~20:太小无法满足吞吐需求,太大会导致消息积压在消费者内存中。初始可按 prefetched messages ≤ 处理耗时 × prefetch_count 来估算。
2. 需要拆分!去重步骤涉及 Redis 分布式锁是串行瓶颈(50ms),应将去重作为独立的第一层消费者队列(快速筛选有效请求),聚合和写库放到第二层队列做批处理。这样可以大幅缓解单机瓶颈。
3. prefetch 过大的问题:Broker 可以积压更多消息到消费者内存,降低每次投递的往返开销但也增加了内存压力、崩溃恢复时的损失量以及处理时间过长导致 unacked 消息占满 prefetch 配额的风险。prefetch 过小的问题:串行节奏限制吞吐量,网络 RTT 开销占比增大。
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖 prefetch 配置、队列拆分策略和大/小 prefetch 权衡三个维度。
## 关联笔记
- [[04.MQ/rabbitmq/消息持久化与可靠性投递]]
- [[04.MQ/rabbitmq/ACK 确认与死信队列]]
- [[04.MQ/rabbitmq/Exchange 路由机制]]