消息队列对比——为什么最终选了 Kafka / Comparing Message Queues and Choosing Kafka
📅 创建时间:2026-05-08 🏷️ 标签:#RabbitMQ #RocketMQ #Pulsar #消息队列 #选型 #延迟消息 #顺序消息 📚 前置知识:[[00-backend-overview]] [[02-mq-kafka]](Kafka 核心机制) 📚 相关知识:[[04-distributed-system]](分布式事务) [[11-architecture-patterns]](事件驱动)
场景:面试官问你,为什么你们选了 Kafka 而不是 RabbitMQ?
┌─────────────────────────────────────────────────────────────┐
│ │
│ 面试官:你为什么选 Kafka? │
│ 面试者:因为...大家都用? │
│ 面试官:你们系统日活多少? │
│ 面试者:5 万。 │
│ 面试官:那你为什么不选 RabbitMQ? │
│ 面试者:... │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
这一章,我们理解每种 MQ 的设计哲学,以及什么场景选什么。
第1节:四强横评——为什么排队这么久
先问自己三个问题
┌─────────────────────────────────────────────────────────────┐
│ 选 MQ 前必须回答的三个问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问题 1:吞吐量多少? │
│ • 日活 100 万以下 → RabbitMQ / RocketMQ 够用 │
│ • 日活 1000 万以上 → Kafka / Pulsar │
│ │
│ 问题 2:需不需要严格顺序? │
│ • 需要同一用户的操作有序 → Kafka / RocketMQ │
│ • 不需要 → RabbitMQ / Pulsar 都可以 │
│ │
│ 问题 3:需不需要事务消息? │
│ • 扣库存 + 创建订单必须原子 → RocketMQ 事务消息 │
│ • 不需要 → Kafka 幂等生产者足矣 │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
第2节:RabbitMQ——"瑞士军刀"式的灵活
核心概念:Exchange + Binding
┌─────────────────────────────────────────────────────────────┐
│ RabbitMQ 路由模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Producer ──▶ Exchange(交换机)──路由规则──▶ Queue ──▶ Consumer
│ │
│ Exchange 类型: │
│ │
│ direct: 精确匹配 routing_key → 指定 Queue │
│ fanout: 广播 → 所有绑定的 Queue │
│ topic: 通配符匹配 (order.*, *.created) │
│ headers: 按消息头属性匹配 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 场景:订单状态变化通知 │ │
│ │ │ │
│ │ Exchange: order.events (topic) │ │
│ │ │ │
│ │ order.created ──▶ Queue: order-created-handlers │ │
│ │ order.paid ──▶ Queue: order-paid-handlers │ │
│ │ order.cancelled ──▶ Queue: order-cancelled-handlers│ │
│ │ order.* ──▶ Queue: order-all-handlers │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
RabbitMQ 的优势:灵活路由
python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
# 声明交换机
channel.exchange_declare(exchange='order.events', exchange_type='topic')
# 声明队列
channel.queue_declare(queue='order-created-handlers')
channel.queue_declare(queue='order-paid-handlers')
# 绑定:Routing Key → Queue
channel.queue_bind(exchange='order.events', queue='order-created-handlers', routing_key='order.created')
channel.queue_bind(exchange='order.events', queue='order-paid-handlers', routing_key='order.paid')
# 生产者:按不同 routing_key 发到不同队列
channel.basic_publish(
exchange='order.events',
routing_key='order.created', # 只发给 created 处理器
body=json.dumps({"order_id": 1, "status": "created"})
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
RabbitMQ 的劣势:吞吐量
┌─────────────────────────────────────────────────────────────┐
│ RabbitMQ vs Kafka 吞吐量对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ RabbitMQ(单节点): │
│ • 吞吐量:5-10 万条/秒 │
│ • 队列个数增加后,性能下降(Erlang 虚拟机瓶颈) │
│ • 队列存储在内存(可选持久化,但性能下降) │
│ │
│ Kafka(3 节点集群): │
│ • 吞吐量:100-500 万条/秒 │
│ • 队列(分区)数增加后,性能稳定 │
│ • 数据持久化到磁盘(通过 PageCache 加速) │
│ │
│ 为什么差距这么大? │
│ RabbitMQ:队列 = 消费者缓冲区(内存) │
│ Kafka:队列 = 磁盘文件(追加写 + PageCache) │
│ │
│ 但:RabbitMQ 的单条消息延迟更低(无磁盘 IO) │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
RabbitMQ 高级特性
1. 消息 TTL
python
# 队列 TTL:队列中所有消息超过这个时间自动删除
channel.queue_declare(queue='order.pending', arguments={
'x-message-ttl': 300000, # 5 分钟
'x-max-length': 1000 # 最多 1000 条
})
# 消息 TTL:单条消息的过期时间
channel.basic_publish(
exchange='',
routing_key='order.pending',
body=json.dumps({"order_id": 1}),
properties=pika.BasicProperties(expiration='60000') # 60 秒
)1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
2. 死信队列(DLX)
python
# 订单超过 5 分钟未支付,自动进入死信队列
channel.queue_declare(queue='order.dlx', arguments={
'x-dead-letter-exchange': 'order.dlx.exchange', # 死信交换机
'x-dead-letter-routing-key': 'order.expired' # 死信路由键
})1
2
3
4
5
2
3
4
5
3. 延迟队列(通过 TTL + DLX 实现)
python
# 订单超时未支付:TTL 5分钟 → DLX → 取消订单处理器
# 实际使用:rabbitmq-delayed-message-exchange 插件
channel.queue_declare(queue='order.timeout', arguments={
'x-delayed-message-type': 'direct',
'x-delayed': 300000 # 5 分钟后投递
})1
2
3
4
5
6
2
3
4
5
6
第3节:RocketMQ——阿里出品,中国互联网实战
RocketMQ 的独特能力
┌─────────────────────────────────────────────────────────────┐
│ RocketMQ vs Kafka 的核心差异 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Kafka 的问题: │
│ ❌ 消息重复消费:at-least-once 设计 │
│ ❌ 事务消息:只保证 Kafka 内部,跨数据库不行 │
│ ❌ 延迟/定时消息:不支持 │
│ ❌ 消费顺序:一个分区只能一个消费者 │
│ │
│ RocketMQ 的答案: │
│ ✅ 消息不可重复:天然幂等 │
│ ✅ 事务消息:half 消息 + 本地事务 + 回查 │
│ ✅ 延迟消息:18 个延迟级别(1s-2h) │
│ ✅ 顺序消息:按用户维度锁定消费 │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
事务消息(核心差异)
┌─────────────────────────────────────────────────────────────┐
│ RocketMQ 事务消息原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 场景:创建订单(MySQL) + 扣减库存(Redis),必须原子 │
│ │
│ Step 1: 发送 half 消息(预发送) │
│ Producer ──▶ RocketMQ ──▶ 返回 ACK(此时消息不可见) │
│ │
│ Step 2: 执行本地事务(MySQL + Redis) │
│ Producer 执行:INSERT orders; Redis.decr(stock); │
│ │
│ Step 3a: 本地事务成功 → 提交 half 消息 │
│ Producer ──▶ RocketMQ: commit ──▶ 消息对消费者可见 │
│ │
│ Step 3b: 本地事务失败 → 回滚 half 消息 │
│ Producer ──▶ RocketMQ: rollback ──▶ 消息删除 │
│ │
│ Step 4: RocketMQ 回查(万一 Producer 挂了?) │
│ Producer 挂了,half 消息超时未提交/回滚 │
│ → RocketMQ 回查 Producer(问:这条消息还要吗?) │
│ → Producer 恢复,检查本地事务状态 │
│ → 决定 commit 或 rollback │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java
// RocketMQ 事务消息(Java)
@Transactional
public void createOrder(OrderDTO dto) {
// Step 1: 发送 half 消息
TransactionMQProducer producer = new TransactionMQProducer("order-producer");
producer.executeLocalTransaction((msg, arg) -> {
// Step 2: 本地事务
try {
orderService.create(dto); // MySQL
stockService.decr(dto.getProductId()); // Redis
return LocalTransactionState.COMMIT_MESSAGE; // 提交
} catch (Exception e) {
return LocalTransactionState.ROLLBACK_MESSAGE; // 回滚
}
}, dto);
// Step 3: 发送业务消息(扣库存指令)
producer.sendMessageInTransaction(msg, dto);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
第4节:Pulsar——云原生的后起之秀
┌─────────────────────────────────────────────────────────────┐
│ Pulsar 分层架构(核心创新) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Broker(无状态服务层) │ │
│ │ • 接收 Producer 消息 │ │
│ │ • 分发消息给 Consumer │ │
│ │ • 无状态!可以随时重启/扩缩容! │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Bookie(Apache BookKeeper,存储层) │ │
│ │ • 消息持久化 │ │
│ │ • 副本机制(默认 3 副本) │ │
│ │ • 可以独立扩缩容 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 对比 Kafka: │
│ Kafka:每个 Broker 既处理请求,又存储数据 │
│ → Broker 挂了,它负责的分区全部不可用 │
│ → 扩容时需要迁移数据(慢!) │
│ │
│ Pulsar:Broker 无状态,扩缩容 不需要迁移数据 │
│ → Broker 挂了,其他 Broker 立即接管 │
│ → 真正云原生 │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
第5节:四强横向对比
┌─────────────────────────────────────────────────────────────┐
│ 消息队列四强对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ │ 维度 │ Kafka │ RabbitMQ │ RocketMQ │ Pulsar │
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 吞吐量 │ 百万级 │ 十万级 │ 十万级 │ 百万级 │
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 顺序消息 │ ✅分区内│ ✅队列内 │ ✅分区内│ ✅分区内│
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 事务消息 │ 仅 MQ 内部│ ❌ │ ✅跨系统│ ❌ │
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 延迟消息 │ ❌ │ ✅插件 │ ✅原生 │ ✅原生│
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 幂等性 │ ✅幂等生产者│ ✅业务层│ ✅业务层│ ✅业务层│
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 消费模式 │ pull │ push/pull│ pull │ push/pull│
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 部署复杂度 │ 中高 │ 低 │ 中 │ 中高 │
│ ├─────────────┼─────────┼──────────┼──────────┼─────────┤
│ │ 生态 │ 成熟 │ 插件多 │ 阿里系 │ 新兴 │
│ │
└─────────────────────────────────────────────────────────────┘
选型建议:
• 超高吞吐量(日活千万+)→ Kafka / Pulsar
• 需要事务消息(跨库原子)→ RocketMQ
• 需要灵活路由(多 Consumer)→ RabbitMQ
• 需要云原生弹性 → Pulsar
• 小型系统,不需要高吞吐 → RabbitMQ(上手最快)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
升华:MQ 选型的本质
┌─────────────────────────────────────────────────────────────┐
│ MQ 选型的三个维度 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 维度 1:吞吐量(决定了你要不要选 Kafka) │
│ → 吞吐量 < 10 万/秒:RabbitMQ / RocketMQ 够用 │
│ → 吞吐量 > 100 万/秒:Kafka / Pulsar 必须 │
│ │
│ 维度 2:功能需求(决定了你要不要选 RocketMQ) │
│ → 需要跨数据库事务 → RocketMQ 事务消息 │
│ → 需要延迟/定时消息 → RocketMQ / RabbitMQ 插件 │
│ │
│ 维度 3:运维能力(决定了你要不要选 Pulsar) │
│ → 有 K8s + 需要弹性扩缩容 → Pulsar │
│ → 简单部署,不想运维太多组件 → RabbitMQ │
│ │
│ 一句话总结: │
│ 99% 的场景,Kafka 都不会是错误的选择。 │
│ 但如果你知道为什么选了 RabbitMQ,你的水平就更高了。 │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"AI 可查 vs 必须理解"清单
AI 可查:
✅ RabbitMQ Exchange 类型的具体配置参数
✅ RocketMQ 事务消息的 Java API
✅ Pulsar Bookie 的配置项
必须理解:
🔴 四种 MQ 的核心差异(不是背表,是理解设计哲学)
🔴 RocketMQ 事务消息的 half 消息 + 回查机制
🔴 Kafka/RocketMQ 顺序消息的"分区内有序"
🔴 吞吐量 > 100 万/秒时为什么不选 RabbitMQ1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
学习状态:🟡 开始学习