多频道订阅时,redis.PubSub消息为字典结构,type字段含"message""pmessage""subscribe"等;须先判断type,仅对"message"和"pmessage"解析;channel和data为bytes需decode;psubscribe时用pattern路由而非channel;生产环境优先用get_message(timeout)而非listen();JSON数据需统一编码与格式校验。

订阅多个频道时,redis.PubSub 的消息结构怎么变?
多频道订阅本身不会改变 Redis 消息的原始格式,但 redis.PubSub 对象返回的每条消息变成一个字典,且 type 字段不再只是 "message" —— 还可能出现 "pmessage"(模式匹配)、"subscribe"、"unsubscribe" 等控制消息。你如果只检查 msg["type"] == "message",会漏掉实际数据,也会误把订阅确认当业务消息处理。
实操建议:
- 必须先判断
msg["type"],只对"message"和"pmessage"做业务解析 -
"message"类型下,msg["channel"]是 bytes 类型(如b"chat:room1"),需 decode;msg["data"]同理 - 用
ignore_subscribe_messages=True初始化PubSub可自动过滤"subscribe"/"unsubscribe",省去手动判断
用 psubscribe 订阅模式时,如何区分不同频道的实际来源?
当你用 psubscribe("chat:*", "log:err*"),所有匹配消息都走 "pmessage" 类型,但 msg["channel"] 是原始发布频道名(如 b"chat:room123"),而 msg["pattern"] 才是匹配所用的模式(如 b"chat:*")。很多人误把 msg["channel"] 当成模式,结果路由错逻辑。
实操建议:
- 解析时优先看
msg["pattern"]判断归属哪类业务流,再用msg["channel"]做二级分发 - 避免在回调里硬编码频道前缀,比如不要写
if b"chat:" in msg["channel"]—— 模式匹配已由 Redis 完成,重复判断冗余且易错 - 若需同时支持精确频道 + 模式订阅,务必在
listen()循环中统一 dispatch,别拆成两个独立PubSub实例(连接和缓冲不共享,会丢消息)
并发多频道订阅下,get_message() 和 listen() 哪个更稳?
get_message() 是非阻塞轮询,适合嵌入已有事件循环(如 asyncio 或 Flask worker);listen() 是阻塞迭代器,底层调 socket.recv(),一旦连接抖动或服务器断连,容易卡死或抛出 ConnectionError,且无法设超时。多频道场景下,后者更难做优雅降级。
实操建议:
- 生产环境一律用
get_message(timeout=0.1),配合 while 循环 + 异常捕获 - 每次调用后检查返回值是否为
None(无新消息),不要直接解包 - 遇到
ConnectionError或TimeoutError,应重建PubSub实例并重订阅(Redis 不保证断线期间的消息可达) - 别在
listen()里加 try/except 包裹整个 for 循环 —— 错误发生时迭代器已失效,继续 next() 会报StopIteration或静默失败
JSON 数据混在不同频道里,解析失败怎么定位?
常见错误现象:json.loads(msg["data"]) 报 UnicodeDecodeError 或 JSONDecodeError,但打印 msg["data"] 看着像正常字符串。根本原因是:有些发布方用 publish("chan", {"a":1}) 直接发 dict(redis-py 自动序列化为字符串),有些用 publish("chan", json.dumps({...})),还有些用 msgpack —— 频道之间协议不统一。
实操建议:
- 在订阅端不做“尽力解析”,而是严格约定:所有频道数据必须是 UTF-8 编码的 JSON 字符串,其他格式拒收
- 统一加一层解析 wrapper:
def safe_json_loads(data):
if isinstance(data, bytes):
data = data.decode("utf-8")
try:
return json.loads(data)
except (UnicodeDecodeError, json.JSONDecodeError) as e:
logger.warning("bad payload on %s: %r, err=%s", msg["channel"], data[:100], e)
return None
真正麻烦的是——不同团队维护不同频道,协议演进不同步。上线前必须核对每个频道的发布方序列化方式,光看文档没用,得抓包或打日志验证。

















