Redis 提供了多种消息通信机制,包括 Pub/Sub(发布订阅)、Blocking Lists(阻塞列表) 和 Stream(流)。它们的设计目标和适用场景有显著区别,下面通过具体示例和对比来说明差异。
一 Pub/Sub(发布订阅)
特点
- 无持久化:消息发送后,如果没有订阅者在线,消息会丢失。
- 实时广播:消息会发送给 “所有” 订阅该频道的客户端。
- 无状态:服务端不记录订阅者的消费状态。
示例:
PUBLISH news "Hello, world!"
SUBSCRIBE news # 订阅者1
SUBSCRIBE news # 订阅者2
适用场景:
- 实时通知(如聊天室、实时日志广播)。
- 事件驱动架构(如微服务间的事件通知)。
- 不需要消息持久化或消费确认。
二 Blocking Lists(阻塞列表)
特点:
- 基于 List 结构,使用
BLPOP
/BRPOP
实现阻塞等待。 - 单消费者模式:一条消息只能被一个消费者获取。
- 支持超时:可以设置阻塞等待的最长时间。
示例:
# 生产者 发消息
lpush myqueue "task1"
lpush myqueue task2
# 消费者 收消息
blpop myqueue 0 # 0 表示无限等待. blpop 每次只取一个元素
适用场景:
- 简单的任务队列(如后台任务分发)。
- 单消费者模式(如订单处理,一个订单只能由一个 worker 处理)。
- 需要阻塞等待新消息(避免轮询)。
三 Stream(流)
特点:
- 持久化存储:消息会保存在 Redis 中,直到被明确删除。
- 消费者组(Consumer Groups):支持多消费者协同消费,每条消息只被组内的一个消费者处理。
- 消息回溯:支持按 ID 范围查询历史消息。
- ACK 机制:消费者必须确认消息处理完成,否则会重新投递。
示例:
XADD mystream * field1 "value1" field2 "value2" # 输出 "1630000000000-0" 为生成消息的 id
XGROUP CREATE mystream mygroup 0 # 创建消费者组(从id=0开始消费)
XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream > # 消费者1 读取消息
# 输出如下:
1) 1) "mystream"
2) 1) 1) "1743405096987-0"
2) 1) "field1"
2) "value1"
3) "field2"
4) "value2"
XACK mystream mygroup 1630000000000-0 # XACK 确认
适用场景
- 消息队列(如订单处理、日志收集)。
- 多消费者协同(如 Kafka 风格的消费者组)。
- 需要消息持久化和消费确认(避免消息丢失)。
四 对比
特性 | Pub/Sub | Blocking Lists | Stream |
---|---|---|---|
持久化 | ❌ 无 | ✅(List 存储) | ✅(Radix Tree 存储) |
消费者模型 | 广播(所有订阅者) | 单消费者 | 消费者组(协同消费) |
是否支持广播 | ✅ 必须广播 | ❌ 不支持 | ✅ group 层支持广播(完整的流数据),同时 consumer 层也支持拆分(部分的流数据) |
消息确认(ACK) | ❌ 不支持 | ❌ 不支持 | ✅ 支持 |
历史消息查询 | ❌ 不支持 | ✅(LRANGE 查询) | ✅(XRANGE 查询) |
阻塞等待 | ✅(实时推送) | ✅(BLPOP/BRPOP) | ✅(XREADGROUP) |
适用场景 | 实时通知、事件驱动 | 简单任务队列 | 复杂消息队列(如 Kafka) |
如何选择?
- 如果需要实时广播 → Pub/Sub(如聊天室)。
- 如果需要简单的任务队列 → Blocking Lists(如订单处理)。
- 如果需要持久化、消费者组、ACK 机制 → Streams(如分布式日志收集)。
Stream 是 Redis 5.0 后引入的最强大的消息队列方案,适合需要可靠性和复杂消费模式的场景,而 Pub/Sub 和 Blocking Lists 更适合轻量级需求。
↑↓⇔⇧⇩