RabbitMQ 与 Kafka 入门教程:任务队列还是事件流?
RabbitMQ 与 Kafka 入门教程:任务队列还是事件流?
做后端时总会碰到这类需求:接口要尽快返回,但后面还有抓取、写日志、发通知等「慢活」。如果全堆在一次 HTTP 请求里,接口会变慢,高峰时还容易把服务打挂。
消息中间件就是把「发出任务」和「真正干活」拆开:业务先把消息丢进中间件,消费者再慢慢处理。市面上最常被拿来对比的两套方案,就是 RabbitMQ 和 Kafka。
本文用尽量少的概念,讲清二者分别擅长什么,以及本站(NestJS 博客)里为什么一个用 RabbitMQ、一个用 Kafka。
1. 先记住一个判断题
| 你更像在做…… | 更合适 |
|---|---|
| 「帮我办一件事」:抓取、转码、发邮件、扣积分 | RabbitMQ(任务队列) |
| 「记下一串发生过的事」:访问日志、埋点、审计流 | Kafka(事件流 / 日志) |
一句话:
- RabbitMQ:消息像待办工单,处理完通常就没了(消费确认后删除)。
- Kafka:消息像账本,写进去后还能按偏移量重放、多组消费者各自读。
两者都能做异步,但心智模型和运维成本不一样。
2. 为什么要用中间件?
没有中间件时,常见写法是:
text
用户请求 → 接口里同步抓网页 → 写库 → 再返回
问题很直观:
- 慢:抓取、第三方 API 一卡,用户就一直转圈。
- 脆:下游挂了,接口也一起失败。
- 难扩:流量突增时,只能把整台 API 机器一起扩,无法单独扩「干活」的进程。
有了队列之后:
text
用户请求 → 入队成功就返回 → 消费者异步处理 → 写库 / 回调
API 只负责「登记任务」,真正耗时的工作交给消费者,峰值被缓冲在队列里。
3. RabbitMQ 入门
3.1 核心概念(够用即可)
text
Producer(生产者)
↓
Exchange(交换机:按规则把消息分发出去)
↓
Queue(队列:真正存消息的地方)
↓
Consumer(消费者:取出并处理)
初学时最常见的模式是:生产者直接往某个队列发消息(内部往往是默认交换机 + 路由键等于队列名)。你只需要记住:
- Queue:待办列表。
- Consumer:工人。多个工人可以抢同一队列的任务(竞争消费)。
- ACK:处理成功再确认;失败可不确认或拒绝,消息可重新入队。
3.2 适合做什么?
- 书签 / 友链全量抓取
- 视频去水印解析
- 邮件、短信、Webhook
- 需要「做完就完」的后台任务
特点是:任务有明确终点,积压可以看队列长度,处理完消息通常不再保留。
3.3 本站里的 RabbitMQ
本站开启 RABBITMQ_ENABLED=true 后,用 AMQP 连接 Broker,主要队列包括:
| 队列名 | 用途 |
|---|---|
blog.crawl.bookmark |
书签全量抓取 |
blog.crawl.friend-link |
友链可达性 / 元信息刷新 |
blog.mp.video-watermark |
小程序视频去水印任务 |
管理后台仪表盘会展示这些队列的积压;健康检查也会探测 RabbitMQ 是否可达。
未配置 RABBITMQ_URL 时,代码会回退到进程内异步,本地开发不装 RabbitMQ 也能跑通主流程。
3.4 本地快速体验(Docker)
bash
docker run -d --name rabbitmq \
-p 5672:5672 -p 15672:15672 \
rabbitmq:3-management
浏览器打开 http://localhost:15672(默认账号密码一般是 guest / guest),就能看到队列、连接和消息吞吐。生产环境请改掉默认账号,并限制管理端口的公网访问。
4. Kafka 入门
4.1 核心概念
text
Producer → Topic(主题,可分成多个 Partition)
↓
Consumer Group(消费组)
关键点:
- Topic:一类事件的名字,例如
blog.api-request-logs。 - Partition:分区。同一分区内有序;多分片可并行消费。
- Offset:消费进度,像书签一样记「读到哪一行」。
- Consumer Group:同一组内的实例分摊分区;不同组可以各自完整消费同一 Topic(互不影响)。
Kafka 把消息更像追加写的日志:默认会保留一段时间(或按体积),而不是「消费完立刻删」。
4.2 适合做什么?
- 高吞吐写入:接口日志、埋点、审计
- 需要回放 / 多下游订阅同一数据流
- 后续接数仓、实时统计、告警分析
不太适合当成「简单任务队列」的首选:运维(ZooKeeper / KRaft、磁盘、分区规划)比 RabbitMQ 重一截。
4.3 本站里的 Kafka
本站接口请求日志原先走 Redis List 批量刷库;接入 Kafka 后变成:
text
请求结束 → produce 到 blog.api-request-logs
→ consumer group blog-api-logs 批量 createMany 入库
相关配置大致是:
env
KAFKA_ENABLED=true
KAFKA_BROKERS=127.0.0.1:9092
KAFKA_CLIENT_ID=blog-api
KAFKA_API_LOG_TOPIC=blog.api-request-logs
KAFKA_API_LOG_GROUP=blog-api-logs
未启用 Kafka 时,自动回退 Redis + 定时 flush,保证日志链路不断。
PM2 多实例时,同一 Consumer Group 会分摊分区,正好匹配「多进程消费同一条日志流」的场景。
5. 一张表对比
| 维度 | RabbitMQ | Kafka |
|---|---|---|
| 模型 | 队列 / 交换机 | 分区日志 |
| 消息寿命 | 通常消费后删除 | 按策略保留,可重放 |
| 吞吐 | 中等,够大多数业务任务 | 更高,偏流式写入 |
| 多订阅者 | 要 fanout / 多队列复制 | 天然:多 Consumer Group |
| 运维复杂度 | 相对轻 | 相对重 |
| 典型场景 | 异步任务、RPC 解耦 | 日志、埋点、事件溯源 |
不是二选一消灭对方:本站就是并存——任务走 RabbitMQ,日志流走 Kafka。
6. 接入时容易踩的坑
-
把 Kafka 当小队列用
只有一两个消费者、消息量不大、也不需要重放时,RabbitMQ(或 Redis 队列)往往更简单。 -
忘记失败处理
RabbitMQ 要设计 ACK / 重试 / 死信;Kafka 要明确「处理失败是否提交 offset」,避免丢消息或死循环。 -
本地和线上行为不一致
本站做法是:中间件可选,未启用则回退。开发机不装 Broker 也能写业务,但联调队列行为时仍建议起 Docker。 -
只入队不消费
CLI 或临时脚本若开启「只发布」,记得别在生产主进程上误关消费者,否则积压会一直涨。 -
安全组与管理台
5672/9092/15672不要无脑对公网全开;管理台建议仅内网或加鉴权。
7. 怎么在自己的项目里落地(最小步骤)
RabbitMQ 任务
- 安装 Broker,创建 vhost / 用户。
- 业务侧:
publish(queue, payload)。 - 消费者:
consume→ 处理 →ack。 - 监控队列长度;积压告警。
Kafka 日志流
- 起 Broker(单节点学习可用 KRaft 或现成发行版)。
- 创建 Topic(分区数按消费者规模预估)。
- 请求链路
produce;独立 consumer group 批量落库。 - 观察 consumer lag(积压 = 日志末端偏移 − 组已提交偏移)。
本站管理后台的「消息队列」面板,就是在看 Rabbit 队列深度和 Kafka lag,方便一眼判断有没有堵。
8. 小结
- 要异步办事 → 先想 RabbitMQ(或同类任务队列)。
- 要高吞吐记账 / 多下游读同一流 → 再上 Kafka。
- 选型看问题形态,不看谁「更流行」。
- 生产环境务必:鉴权、限端口、可回退、可观测(积压 + 健康检查)。
如果你正在搭个人站点或中小型 NestJS 服务,完全可以像本站一样:RabbitMQ 扛爬虫与工具任务,Kafka 扛接口日志,两套职责清晰,出问题也好分别排查。
文中队列名、Topic 名对应本站当前实现;若你 fork 代码,以仓库 docs 与环境变量为准。

评论
后参与评论
暂无评论,来抢沙发吧