Redis发布订阅模式如何实现消息队列?

来源:AI教程网作者:USDT程序员头衔:程序员
导读:本期聚焦于USDT程序员创作的《Redis发布订阅模式如何实现消息队列?》,敬请观看详情。Redis 的发布订阅机制到底能不能直接当成消息队列来使用?如果把 PUBLISH 当作生产者、SUBSCRIBE 当作消费者,确实可以快速搭建一个实时消息通道,但它和传统消息队列在可靠性上有明显差距。文章会从 Redis Pub/Sub 的频道订阅与模式订阅讲起,演示基于 Python 和 Node.js 的轻量级消息队列实现,并分析消费者离线丢消息、无确认机制、无持久化等核心限制。同时对比 List、Stream 等可持久化队列方案,给出选型建议。读完可以明确哪些业务适合用 Pub/Sub,哪些必须换成更可靠的消息系统。

Redis 提供了一套原生的发布订阅机制,它通过频道(Channel)把消息从发布者传递到订阅者。与消息队列常见的先存储再消费模式不同,Pub/Sub 在消息发布时立即遍历当前所有订阅该频道的客户端连接,把消息直接推送给它们。整个过程中 Redis 不会持久化消息,也不会记录哪些客户端曾经订阅过。这一点决定了它适合做实时通知类的消息分发,而用做高可靠消息队列时需要做很多额外设计。下面先从核心命令和订阅行为开始分析。

Redis发布订阅模式如何实现消息队列?

一、频道订阅与模式订阅的工作方式

Redis Pub/Sub 由几个基础命令组成:SUBSCRIBE 用于订阅一个或多个频道,PUBLISH 用于向指定频道发送消息,UNSUBSCRIBE 用于退订。订阅一旦建立,客户端会进入阻塞状态,持续接收来自服务端的消息推送。

除了精确匹配频道名,Redis 还提供模式订阅命令 PSUBSCRIBE,支持通配符匹配,比如订阅 news.* 可以接收所有以 news. 开头的频道消息。模式订阅在某些广播场景非常有用,但也要注意它会在服务端做模式匹配,订阅模式过多时可能增加 CPU 消耗。

# 客户端1:订阅频道
SUBSCRIBE order_notify
# 客户端2:向频道发布消息
PUBLISH order_notify "hello redis"

当执行 PUBLISH 时,Redis 会返回一个整数,表示有多少个订阅者收到了这条消息。如果返回 0,说明当前没有在线订阅者,消息随即被丢弃,不会保留在 Redis 中。这是理解 Pub/Sub 消息队列定位的关键:它只负责投递给当下正在监听的消费者。

二、基于Python和Node.js实现消息队列

在轻量级应用中,可以用一个生产者线程定时发布任务消息,一个或多个消费者进程订阅频道并处理。下面是一段 Python 示例,生产者生成 JSON 格式的订单通知,消费者订阅后解析消息。

import redis
import json
import time

def producer():
    client = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)
    for i in range(1, 6):
        msg = json.dumps({'order_id': i, 'action': 'created'})
        receivers = client.publish('order_notify', msg)
        print(f'已发送第{i}条消息,接收者数量:{receivers}')
        time.sleep(1)
    client.close()

if __name__ == '__main__':
    producer()

消费者需要保持长连接,并进入消息循环。Python 的 redis 库提供了 pubsub() 方法来获取消息迭代器,可以逐条读取订阅到的消息,再交给业务函数处理。

import redis
import json

def handle_message(data):
    print(f'处理消息:{data}')

def consumer():
    client = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)
    pubsub = client.pubsub()
    pubsub.subscribe('order_notify')
    for item in pubsub.listen():
        if item['type'] == 'message':
            data = json.loads(item['data'])
            handle_message(data)

if __name__ == '__main__':
    consumer()

从上面代码可以看出,消费者通过 listen() 阻塞获取消息,收到 message 类型后解析 JSON 并处理。这种实现结构简单,适合内部事件通知。但必须注意,如果消费者在消息发布时还没有启动,那么这条消息就彻底丢了,订阅者无法像读队列一样补读历史数据。

Node.js 方案也类似,使用 ioredis 客户端创建订阅连接,监听 message 事件即可。由于 Node.js 的事件驱动模型天然适合长连接推送,前端通知类服务经常采用这种组合。

const Redis = require('ioredis');
const publisher = new Redis();
const subscriber = new Redis();

subscriber.subscribe('order_notify', (err, count) => {
  if (err) console.error(err);
  console.log(`已订阅 ${count} 个频道`);
});

subscriber.on('message', (channel, message) => {
  console.log(`收到来自 ${channel} 的消息:${message}`);
  // 执行具体的业务处理
});

// 模拟生产者发送消息
setInterval(() => {
  publisher.publish('order_notify', JSON.stringify({ time: Date.now() }));
}, 2000);

这段代码中的箭头函数使用了 =>,实际运行时需要确保 Node.js 版本支持 ES6。发布端和订阅端建议使用两个独立的 Redis 连接,因为订阅连接进入订阅模式后通常不能再执行其他命令。虽然 Redis 客户端允许复用连接,但混用发布和订阅命令容易造成协议混乱,所以分开连接是更稳妥的做法。

三、Pub/Sub做消息队列的四个关键缺陷

第一是消息无法持久化。Redis Pub/Sub 不把消息写入磁盘或内存队列,一旦发布时没有订阅者在线,消息直接消失。即使有订阅者,消息推送到客户端后也不会保留副本,消费者若处理失败无法重新读取。

第二是没有确认与重试机制。传统消息队列在消费者处理成功后发送 ACK,未确认的消息会重新投递;Pub/Sub 推出去就结束了,Redis 不关心业务是否处理成功。第三是消费者离线期间的消息不可恢复,它不像 Kafka 那样保留一段时间内的消息供消费者重新消费。第四是顺序性只对单连接成立,多个订阅者之间无法保证全局有序。

举一个真实场景:订单服务发布订单创建事件后,通知服务恰好正在进行滚动重启。重启窗口只有几秒钟,但这段时间内发布的事件没有任何订阅者接收,业务就会出现漏发短信或漏更新看板的问题。即使消费者启动后重新订阅,Redis 也不会把之前的消息补给它。这种丢失在高峰期可能被忽略,但一旦涉及资金或库存就会造成明显数据不一致。

这四点决定了 Redis Pub/Sub 不适用于金融交易、订单状态流转、任务调度等对可靠性要求高的场景。更适合实时通知、在线状态广播、聊天消息推送、配置实时下发等允许少量丢失的轻量级事件分发。

四、用Stream或其他方案增强可靠性

如果业务既想用 Redis 又要可靠队列,优先考虑 Redis Stream。Stream 支持消费者组、ACK 确认、消息持久化和阻塞读取,基本具备一个消息队列所需的能力。下面是一个使用 Python 操作 Stream 的简化示例。

import redis

client = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)

# 生产者写入消息
client.xadd('order_stream', {'order_id': '1001', 'action': 'created'})

# 消费者读取消息
messages = client.xread({'order_stream': '0'}, count=10, block=1000)
for stream, entries in messages:
    for entry_id, fields in entries:
        print(entry_id, fields)
        # 确认处理完成
        client.xack('order_stream', 'my_group', entry_id)

Stream 的 xread 只是简单读取,真正可靠消费需要创建消费者组并使用 xreadgroup,这样未确认的消息会保留在 Pending 列表中,消费者崩溃后可以被其他成员接管。这是 Pub/Sub 完全不具备的能力。

除了 Stream,Redis List 配合 BLPOP 或 BRPOP 也能实现简单的阻塞队列,消息会保存在列表中,客户端可以按顺序取出。但 List 不支持消费者组和广播推送,只适合单消费者或手动分发模式。如果消息量较大且需要广播,Pub/Sub 仍有优势。

五、选型建议与总结

综合来看,Redis Pub/Sub 实现消息队列的最大价值是简单和低延迟。它不落盘,不保存历史,推送路径短,适合对实时性要求高、允许丢失的广播类场景。比如聊天室实时消息、在线用户状态广播、配置热更新、日志实时推送等。

而一旦业务要求消息不丢、可重试、可追踪,就要切换到 Redis Stream、RabbitMQ 或 Kafka。Redis Stream 适合性能要求高且希望继续用 Redis 作为数据通道的团队,RabbitMQ 的交换机模型适合复杂路由,Kafka 适合大规模日志和事件流处理。选择时需权衡持久化需求、吞吐量、运维成本和团队熟悉度。

理解 Pub/Sub 的投递模型和边界,比记住几个命令更重要。它本身不是为消息队列设计,但作为轻量级实时消息通道,在正确的场景下依然非常实用。

Redis发布订阅消息队列Redis修改时间:2026-10-02 06:30:13

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1002/64546.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。