一、什么是 RabbitMQ 的预取值?
在使用 RabbitMQ 的 消费者确认机制(ACK) 时,RabbitMQ 会按照一定策略分配消息给消费者。
预取值(prefetch) 的作用就是:
控制消费者在未确认(unacked)消息的数量上限 。
达到上限之前,RabbitMQ 会继续给该消费者分发消息;达到上限后,不再推送新消息。
简单说:
prefetch 决定了"消费者最多能同时处理多少条消息"。
二、为什么需要设置 prefetch?
如果不设置 prefetch 或设置为默认值 0(无限),RabbitMQ 会不断推消息给消费者 ------ 只要它"空着"。这会带来两个问题:
1. 单一消费者被塞满
某个消费者可能瞬间积压几十几百条消息,压力大。
2. 不公平分发(slow consumer problem)
一个慢消费者因为积压太多未 ACK 的消息,会拖累整个队列的消费效率。
3. 对内存和业务系统不友好
业务方可能一次性处理几十条消息导致:
- 线程池爆满
- 内存飙升
- 响应变慢
三、channel.basicQos 中 prefetch 的三种模式
调用方式通常是:
java
channel.basicQos(prefetchCount);
channel.basicQos(prefetchCount, global);
1. prefetchCount(最常用)
表示给 当前消费者 允许的最大未确认消息数。
示例:
java
channel.basicQos(1);
含义:
每次只分一条消息给消费者,消费者处理完并 ACK 之后,再发下一条。
非常适合:
- 处理较重的任务
- 保证负载均衡
- 保证严格顺序处理
2. prefetchSize(几乎不用)
控制未 ACK 的消息总字节数上限。
因为消息大小通常不确定、配置复杂,因此多数场景忽略此参数。
3. global 参数(容易理解错)
java
channel.basicQos(prefetchCount, true);
global = true 表示:
限制作用范围是 channel 级别(对该 Channel 的所有消费者共享)
global = false(默认)
限制作用范围是 每个消费者自己独立计算。
大多数情况下使用 global = false。
四、prefetch 应该设置多少?(核心部分)
不同场景最佳设置不同。
1. 业务处理较慢 / CPU 密集型
推荐:
prefetch = 1
每个消费者每次只拿一条,减少堆压。
例如:
- 图片处理
- 视频转码
- 复杂计算任务
2. I/O 密集型业务(等待时间多)
推荐:
prefetch = CPU 核心数 × 2 ~ 10
原因:业务处理主要等待数据库、外部接口,有空闲能力继续消费。
3. 高吞吐批处理场景
可以适当开大,例如:
prefetch = 50、100 或更多
但注意不要给消费者造成内存压力。
4. 同一队列多个消费者抢占 fairness(公平性)
不公平分发通常这样解决:
prefetch = 1
确保慢消费者不会积压消息。
五、示例:Spring AMQP 如何设置 prefetch?
1. 简单方式
yaml
spring:
rabbitmq:
listener:
simple:
prefetch: 1
2. 手动配置
java
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setPrefetchCount(1);
六、常见误区总结
❌ 误区 1:prefetch 越大越好
错!
越大消费者积压的未 ACK 消息越多,占内存越高,也可能导致不公平分发。
❌ 误区 2:prefetch=0 表示只拿 1 条
不对。
prefetch=0 表示不限制(无限制)。
❌ 误区 3:global=true 能让所有消费者共享 count
不是共享"所有消费者",只是共享一个 Channel 的消费者。
不同 Channel 不共享。
七、总结
| 场景 | 推荐 prefetch 值 |
|---|---|
| CPU 密集型 | 1 |
| 业务处理较慢 | 1 |
| IO 密集型 | 2~10 |
| 高并发批处理 | 50+ |
| 想要最公平分发 | 1 |
一句话总结:
prefetch = 合理限制消费者"未完成的工作量"。用好了可以提升系统吞吐、稳定性与公平性。