Redis Pub/Sub 是瞬时广播机制,不支持消息持久化、断线重连和ACK确认;可靠多对多推送需结合LIST或STREAM等数据结构自行实现历史拉取、重复过滤与消费确认。

Redis 的 Pub/Sub 本身不支持多对多消息的可靠投递,它只是“广播”——发出去就不管接收方是否在线、是否处理成功。 如果你指望靠 publish + subscribe 实现带历史回溯、断线重连、消息确认的群聊或通知系统,会踩坑踩得很实。
Pub/Sub 的本质是「瞬时广播」,不是「消息队列」
Redis 的 PUBSUB 系统没有存储能力,所有消息只在内存中过一遍:
- 订阅者必须在
publish之前已执行subscribe,否则收不到 - 客户端断开连接后,错过的消息彻底丢失,
redis-cli或任意客户端都无从恢复 - 没有 ACK 机制,无法知道谁收到了、谁没收到、谁处理失败了
- 频道(channel)名不区分大小写,但建议统一小写避免混淆,比如用
group:1024而非GROUP:1024
想真正做多对多推送?得补上「状态 + 存储 + 拉取」三层
典型做法是组合使用 Pub/Sub + Redis 数据结构(如 LIST、STREAM)来模拟可靠通道。例如:
- 每次
publish同时把消息LPUSH到一个group:1024:history的列表里,保留最近 100 条 - 新成员加入群组时,先
LRANGE group:1024:history 0 -1拉取历史,再subscribe group:1024接收后续广播 - 用
STREAM替代LIST可支持消费者组(XGROUP),实现多实例负载分摊和消息确认(XACK) - 注意:STREAM 是 Redis 5.0+ 才有,低版本只能靠 LIST + 自增 ID 模拟,但没法自动去重或标记已读
PHP/Python/Node.js 中容易忽略的连接生命周期问题
很多人写完 subscribe 就以为万事大吉,其实 Pub/Sub 连接非常脆弱:
- PHP 的
redis->subscribe()是阻塞调用,不能混在 Web 请求里跑,必须单独起常驻进程(如用supervisord管理) - Python 的
pubsub.listen()也是阻塞迭代器,一旦网络抖动断开,不会自动重连,得自己捕获ConnectionError并重建pubsub实例 - Node.js 的
redis客户端(v4+)默认启用enable_offline_queue: false,断线期间发的publish会直接报错,不是静默丢弃 - 所有语言里,
subscribe后不能再发其他命令(如GET、SET),否则 Redis 会返回错误ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context
真正难的不是怎么发消息,而是怎么让每个接收方都「感知到自己该收哪几条」「断线后不漏」「重复连接不重复处理」——这些逻辑全得自己兜底,Redis 只负责最快那 10ms 的广播动作。











