redis pub/sub 不保证消息可靠交付,本质是“即发即弃”设计:发布者不感知订阅状态,无持久化、无确认、无重试;消息丢失不报错,只能通过两端计数差或订阅端omem飙升、断连日志间接识别;真需不丢消息,应改用redis stream或rabbitmq等可靠方案。

Redis 的 pub/sub 在 Python 中用起来很轻,但只要业务要求“消息不能丢”,它就不是开箱即用的方案——本质是设计上不保证可靠性,不是你代码写错了。
订阅连接必须独占且常驻
Redis 要求 subscribe 客户端连接不能混用其他命令,否则会报错或阻塞失效。一旦你在同一个 redis.Redis 实例上调用 pubsub() 后又执行 get() 或 set(),连接大概率中断。
- 必须为订阅单独创建
redis.Redis实例,不要复用发布者连接 -
pubsub.listen()是阻塞迭代器,得放在独立线程或 asyncio task 里跑,不能卡住主线程 - 别在
listen()循环里做耗时操作(比如写数据库、调 HTTP),否则缓冲区积压触发client-output-buffer-limit断连
消息丢失不会报错,也查不到日志
PUBLISH 返回值永远是订阅者数量(int),哪怕一个都没连上也返回 0 —— 它不认为这是错误。订阅端断连时,Redis 不通知发布者,也不记录丢弃了哪条消息。
- 无法靠异常捕获来发现丢失,只能靠两端计数对比:发布端本地递增
sent_count,订阅端递增recv_count - 订阅端重连后,
pubsub.subscribe()不会补发断连期间的消息,因为 Redis 根本没存 - 如果看到订阅日志里反复出现
Connection closed by server或client-output-buffer limit,基本就是缓冲区溢出导致丢消息
配置项 client-output-buffer-limit pubsub 很关键
这个参数控制每个订阅连接能缓存多少未消费消息,默认是 pubsub 32mb 8mb 60(硬限制 32MB,软限制 8MB 持续 60 秒)。超出就强制断连。
- 生产环境建议调高:例如
CONFIG SET client-output-buffer-limit "pubsub 256mb 64mb 60" - 但调太高治标不治本——只是给慢消费者多留点时间,不是持久化
- 该配置是运行时生效,重启 Redis 会还原,需写入
redis.conf持久化
真要不丢消息,别硬扛 pub/sub
如果你的场景涉及订单、支付、状态同步,或者 QA 明确说“一条都不能少”,那就别在 pub/sub 上加补偿逻辑了——它没 ACK、没 offset、没重试,所有补救都绕不开自己造轮子。
- 优先换
Redis Stream:用xadd+xreadgroup,支持消费者组、pending list 和手动xack - 单机轻量级替代:用
lpush+brpop模拟队列,至少消息进内存不丢 - 跨服务可靠场景:直接上 RabbitMQ 或 Kafka,省去自研运维成本
记住:pub/sub 的价值在于低延迟广播,不是可靠投递。用错地方,排查成本远高于换方案的成本。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











