消息队列
消息队列(Message Queue, MQ)可以理解为
把 “要做的事” 先存起来,等有人了再慢慢处理,而不是立即执行
和延迟队列得不同
- 消息队列:现在就排队,等待有时间立刻处理
- 延迟队列:到了时间才允许处理
为什么需要消息队列?
如果没有MQ,你的系统是这样的:
用户请求 → 直接调用业务 → 立刻执行比如:
下单 - 扣库存 - 发短信 - 发优惠卷 - 调支付
问题来了:所有的事情都要 同步完成
这会导致
- 接口慢(由于阻塞一个调用可能要等几百毫秒甚至几秒)
- 系统耦合严重
- 某个服务挂掉 - 整体失败
消息队列的核心思想
变成:
用户请求 → 发送消息 → MQ存起来 → 消费者慢慢处理结构:
生产者 → MQ → 消费者举个栗子
没有MQ
用户注册:
用户点击注册
↓
保存用户信息
↓
发送欢迎邮件
↓
发送短信验证码
↓
赠送新人积分
↓
赠送新人优惠券
↓
记录运营日志
↓
注册成功
→ 用户要等全部完成有MQ
用户注册:
用户注册
↓
保存用户
↓
发送MQ消息
↓
立即返回注册成功(100ms)
↓
后台异步处理:xiaojian
├─ 发邮件
├─ 发短信
├─ 加积分
├─ 优惠券
└─ 写日志在完成必须立刻完成的任务后,把其他任务交给后台异步处理。
消息队列解决了什么问题?
异步解耦
例如:在京东进行下单,下单成功后会发送短信。
订单系统 不直接依赖 短信系统
削峰填谷
100万请求 → MQ排队处理
避免同时请求数量太多,导致数据库直接崩溃。
流量削峰
一般做秒杀时会用到
瞬间10万请求 → MQ慢慢处理
可靠性
消息可以重试,不会丢
消息队列 vs 延迟队列
| 功能 | Redis延迟队列 | MQ |
|---|---|---|
| 延迟任务 | 可以 | 原生支持 |
| 解耦 | 一般 | 强 |
| 削峰 | 一般 | 很强 |
| 可靠性 | 中等 | 高 |
| 复杂业务 | 不适合 | 适合 |
Redis MQ
Redis 也可以当作 MQ 来使用,不过它不是专业 MQ,中小项目很常见,大型系统一般会使用 RabbitMQ、RocketMQ 或 Kafka。
Redis 怎么实现 MQ?
Redis 常见有三种方案
| 方案 | 数据结构 | 推荐程度 |
|---|---|---|
| List | List | ★★★ |
| Pub/Sub | 发布订阅 | ★★ |
| Stream | Stream | ★★★★★ |
List 实现消息队列
生产者:
bash
LPUSH order_queue "order:1001"
LPUSH order_queue "order:1002"消费者:
bash
BRPOP order_queue 0含义:没有消息就阻塞等待,有消息时立刻返回。
返回:
1) order_queue
2) order:1001Pub/Sub
发布(生产):
bash
PUBLISH new "hello"订阅(消费):
SUBSCRIBE news收到:
hello但是发布订阅会有一些问题,如果消费者掉线,消息会直接丢失,因此一般不做订单、支付等业务。
适合
- 聊天室
- 在线通知
- 实时广播
Stream(Redis 5)
这是 Redis 官方专门做 MQ 的。
生产:
bash
XADD order_stream * orderId 1001消费:
bash
XREAD COUNT 1 STREAMS order_stream 0支持:
- 消息持久化
- 消费组
- ACK确认
- 重试
- 未消费消息恢复
Redis MQ 和 RabbitMQ 对比
| 功能 | Redis List | Redis Stream | RabbitMQ |
|---|---|---|---|
| 简单队列 | ✔ | ✔ | ✔ |
| 持久化 | Redis持久化 | ✔ | ✔ |
| ACK确认 | ❌ | ✔ | ✔ |
| 消费组 | ❌ | ✔ | ✔ |
| 重试机制 | ❌ | ✔ | ✔ |
| 延迟消息 | 自己实现 | 自己实现 | 原生支持 |
| 专业MQ能力 | 一般 | 较强 | 很强 |
Redis MQ 使用场景
适合:
- 异步发邮件
- 异步发短信
- 日志处理
- 积分处理
- 小型系统任务队列
不适合:
- 支付系统
- 订单系统
- 金融系统
- 交易系统
Spring Boot 示例
List 实现
生产:
java
redisTemplate.opsForList().leftPush("order_queue", "order:1001");消费:
java
while (true) {
Object msg = redisTemplate.opsForList().rightPop("order_queue", 0, TimeUnit.SECONDS);
System.out.println(msg);
}TIP
Redis 可以实现消息队列,其中 List 最简单,Stream 最完整。但 Redis 的核心定位仍然是缓存数据库,不是专业 MQ。对于可靠性要求较高的业务,一般会选择 RabbitMQ、RocketMQ 或 Kafka。