Skip to content

消息队列

消息队列(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 常见有三种方案

方案数据结构推荐程度
ListList★★★
Pub/Sub发布订阅★★
StreamStream★★★★★

List 实现消息队列

生产者:

bash
LPUSH order_queue "order:1001"
LPUSH order_queue "order:1002"

消费者:

bash
BRPOP order_queue 0

含义:没有消息就阻塞等待,有消息时立刻返回。

返回:

1) order_queue
2) order:1001

Pub/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 ListRedis StreamRabbitMQ
简单队列
持久化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。