redis发布订阅不是消息队列,而是轻量级广播机制,消息不持久、不排队、不确认,适合实时通知(如刷新缓存、踢会话),但不可靠,无法替代kafka等实现至少一次交付。

发布订阅不是消息队列,别当可靠传输用
Redis 的 PUBLISH/SUBSCRIBE 本质是广播机制,消息不持久、不排队、不确认,一旦订阅者离线或网络抖动,消息就丢了。它适合控制服务“触发动作”,比如刷新缓存、踢掉会话、通知前端重连,但不能替代 BLPOP + LPUSH 或 Kafka 做订单处理、支付回调这类必须“至少一次”交付的流程。
用 PSUBSCRIBE 实现动态指令分发
硬编码频道名(如 SUBSCRIBE order.created)会让服务耦合变高。更灵活的做法是按业务域定义模式,比如所有支付相关指令走 pay.*,所有用户操作走 user.*:
-
PSUBSCRIBE pay.*让支付服务监听所有以pay.开头的频道 - 发布端执行
PUBLISH pay.refund "order_id=12345&reason=timeout",订阅者自动收到 - 模式匹配支持
?(单字符)和*(任意长度),但不支持正则,别写pay.[refund|cancel]
注意:PSUBSCRIBE 和 SUBSCRIBE 不能混用在同一连接中;退订要用 PUNSUBSCRIBE,不是 UNSUBSCRIBE。
客户端阻塞问题:别让一个 SUBSCRIBE 占满整个连接
SUBSCRIBE 后 Redis 客户端进入阻塞状态,无法再发其他命令。常见错误是:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 在同一个连接里先
GET user:123,再SUBSCRIBE task.done→ 第二条命令根本发不出去 - 用同步客户端(如 Jedis 默认配置)监听频道,导致线程卡死
正确做法:
- 单独开一个连接专用于订阅,用独立线程或异步回调处理消息
- Python 的 redis-py 支持
pubsub()对象,调用pubsub.subscribe("channel")后用listen()迭代器消费 - Node.js 的 ioredis 提供
psubscribe()+on("pmessage")事件,天然非阻塞
中文乱码?不是 Redis 问题,是终端/客户端解码方式不对
看到 \xe4\xbd\xa0\xe5\xa5\xbd 这类输出,说明客户端把 UTF-8 字节流当 ASCII 打印了。这不是 Redis 存错了,而是读取时没指定编码:
- 命令行调试时加
--raw参数:redis-cli --raw SUBSCRIBE chat - Java 用 Jedis 时,确保构造
Jedis实例时传入new JedisPool(...)而非默认构造,避免底层 byte[] 转 String 出错 - Go 的 go-redis 库默认用 UTF-8 解码,只要你的消息本身是 UTF-8 编码(如 JSON 字符串),就不会乱码
真正容易被忽略的是:频道名本身也参与传输,如果用中文命名频道(如 SUBSCRIBE 用户登录),某些旧版客户端可能解析失败,建议统一用英文+点号分隔(user.login)。










