主题
23 · RabbitMQ 入门
目标:弄清 Exchange / Queue / Binding / ACK,用 Go 跑通「发布 → 消费」与至少一种路由模式。
前置:22 · Docker 与 Compose · 21 · Redis 场景
官方:RabbitMQ Tutorials · AMQP 0-9-1 · Go:amqp091-go
1. 为何在 Redis 之后学 MQ
| 场景 | Redis 够用吗 | 何时上 Rabbit |
|---|---|---|
| 缓存、计数、锁、短 Pub/Sub | 够 | — |
| 削峰、跨服务解耦、可重试投递 | 勉强能凑 | 更合适 |
| 要路由语义(按类型分发) | 弱 | Exchange 路由是强项 |
Node 侧印象:Bull / 自写队列 / amqplib。
本篇先建立 AMQP 心智,Asynq(24)再做「Redis 上的 job 队列」。
2. 核心概念(必背)
text
Producer → Exchange --binding→ Queue → Consumer
│
└─ routing key / header 决定进哪条队1
2
3
2
3
| 概念 | 一句话 |
|---|---|
| Exchange | 交换机:决定消息怎么路由,不存消息 |
| Queue | 队列:真正缓冲消息 |
| Binding | Exchange ↔ Queue 的路由规则 |
| ACK | 消费者确认;未 ACK 可重投 |
| Prefetch | 一次未确认条数上限,控压 |
Exchange 类型(先记这三种)
| 类型 | 行为 | 入门用途 |
|---|---|---|
direct | routing key 精确匹配 | 按任务类型分发 |
fanout | 广播到所有绑定队列 | 通知多消费者 |
topic | * / # 模式匹配 | 按主题订阅 |
3. Compose 起 RabbitMQ
在练手仓 docker-compose.yml 追加:
yaml
rabbitmq:
image: rabbitmq:3.13-management-alpine
ports:
- "5672:5672"
- "15672:15672" # 管理台 guest/guest(仅本地)1
2
3
4
5
2
3
4
5
bash
docker compose up -d rabbitmq
# 浏览器 http://127.0.0.1:156721
2
2
4. 最小可跑 demo(direct)
bash
go get github.com/rabbitmq/amqp091-go1
发布:
go
conn, err := amqp.Dial("amqp://guest:guest@127.0.0.1:5672/")
fail(err)
defer conn.Close()
ch, err := conn.Channel()
fail(err)
defer ch.Close()
q, err := ch.QueueDeclare("notes.export", true, false, false, false, nil)
fail(err)
err = ch.PublishWithContext(ctx, "", q.Name, false, false, amqp.Publishing{
DeliveryMode: amqp.Persistent,
ContentType: "application/json",
Body: []byte(`{"noteId":"n1"}`),
})
fail(err)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
默认 Exchange(空字符串)+ routingKey = 队列名 = 最简「直投队列」。
消费(手动 ACK):
go
_ = ch.Qos(1, 0, false) // prefetch=1
msgs, err := ch.Consume("notes.export", "", false, false, false, false, nil)
fail(err)
for d := range msgs {
log.Printf("got %s", d.Body)
// 业务成功再 Ack;失败可 Nack(requeue)
_ = d.Ack(false)
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
topic 一眼版
go
_ = ch.ExchangeDeclare("notes.events", "topic", true, false, false, false, nil)
_ = ch.QueueBind(q.Name, "note.*.exported", "notes.events", false, nil)
_ = ch.PublishWithContext(ctx, "notes.events", "note.pdf.exported", false, false, pub)1
2
3
2
3
5. 动手清单
- [ ] Compose 起 Rabbit;管理台能看到队列
- [ ] 发布 1 条 → 消费者打印 → ACK
- [ ] 消费中途
kill:消息回队列(未 ACK) - [ ] 换
topic:两条不同 routing key,只收到匹配的 - [ ] 能口述:Exchange 与 Queue 谁存消息
6. 项目驱动(手帐场景)
| 场景 | 建议 |
|---|---|
| 导出 PDF / 压缩图 | notes.export 队列;HTTP 只入队返回 202 |
| 多端通知 | fanout 或 topic:note.*.created |
| 失败重试 | Nack + 死信队列(DLX)进阶扫一眼即可 |
与 Redis 分工:热点读写仍 Redis;跨服务、要可靠投递、要路由再 Rabbit。
7. 常见坑 + AI 审查
| 坑 | 说明 |
|---|---|
自动 ACK(autoAck=true) | 业务失败也丢消息;入门请手动 ACK |
忘记 Qos | 一个消费者可能被瞬间灌满 |
| 连接当 Channel 用 | Channel 轻量;连接复用、按协程开 Channel |
| guest 上生产 | 仅本地;生产换账号与 TLS |
| AI 写「发完就关连接」 | 长驻 worker 应复用;短脚本可关 |
8. 下一篇
Redis 上更「像 Bull」的任务队列:
→ 24 · Asynq 任务队列
