redis pub/sub 在 docker 中不通,首要验证通路:用 redis-cli 直连 subscribe/publish 测试,再查 pubsub numsub 确认订阅者在线,结合 --raw --monitor 抓包定位,同时排查 docker 网络配置(如 host 模式、bridge 延迟、ip 目标错位)导致的连接或延迟问题。

Redis PUB/SUB 在 Docker 里不通,八成不是代码写错了,而是网络或配置卡在底层——先别改应用,用 redis-cli 直连验证通路最省时间。
用两个 redis-cli 终端直接测通路
这是最不可跳过的一步。很多人在 Python 或 Node.js 里调了 PUBLISH 却收不到,根本原因是连 Redis 都没真正连上频道。
- 新开终端 A,执行:
redis-cli -h 192.168.1.100 -p 6379 SUBSCRIBE log:app-prod(把 IP 和频道名换成你实际用的) - 新开终端 B,执行:
redis-cli -h 192.168.1.100 -p 6379 PUBLISH log:app-prod '{"msg":"test"}' - 如果终端 A 立即输出三行:
"message"、"log:app-prod"、"{\"msg\":\"test\"}",说明 Redis 层面完全通畅 - 若没反应:检查 IP 是否为宿主机真实地址(不是容器内
localhost)、端口是否映射正确(-p 6379:6379)、Redis 是否启用了密码(需加-a yourpass)
查频道有没有人挂着:PUBSUB NUMSUB 是第一眼诊断工具
PUBSUB NUMSUB 不看消息内容,只告诉你“谁在线”,但足够暴露绝大多数部署问题。
- 在任意
redis-cli里执行:PUBSUB NUMSUB log:app-prod - 返回类似
1) "log:app-prod" 2) (integer) 0?那说明当前没有任何客户端成功订阅该频道——哪怕你的代码写了sub.subscribe("log:app-prod"),也可能因异常、未执行到、或连接复用错误而失效 - 返回
(integer) 1或更高,才代表有活跃订阅者;注意这个值不是实时更新的,客户端断开后 Redis 通常要几秒才归零 - 再跑一次
PUBSUB CHANNELS,确认你的频道名是否出现在列表里;如果不在,说明压根没人SUBSCRIBE过它
用 --monitor 实时抓包,但必须加 --raw 且防截断
MONITOR 是唯一能看到原始 PUBLISH 流量的方式,但它默认输出是转义字节流,不加参数根本看不出发了啥。
- 正确命令:
redis-cli -h 192.168.1.100 -p 6379 --raw --monitor | grep "PUBLISH.*log:app-prod" -
--raw关键:否则中文或 JSON 会显示成\xe4\xbd\xa0\xe5\xa5\xbd,误判为乱码 - 单条消息超长会被截断(默认 512 字节),如果日志字段多,
grep可能匹配不到完整内容;此时得去 Redis 配置里调大proto-max-bulk-len并重启 -
--monitor不重连,断开后不会自动恢复;脚本化使用必须套while true; do ...; sleep 1; done - 它不区分频道,所有
PUBLISH都混在一起,所以一定要配合grep过滤,注意 shell 中正则特殊字符如.要转义
Docker 网络导致监听失败的典型表现
即使 redis-cli 命令能通,应用里还是收不到——大概率是容器网络配置让客户端连到了错的 Redis 实例,或者延迟高到 listen() 超时断开。
- Python 客户端用
pubsub.listen()却没输出?先确认它连的是宿主机 Redis(host="host.docker.internal"或"172.17.0.1"),而不是容器名或localhost(后者在容器里指向自己) - 用
docker run --network host启动应用容器时,Redis 客户端必须连localhost:6379;连容器 IP 或服务名反而失败 - 默认
bridge网络下,PUBSUB延迟可能达 6–12ms,而listen()默认心跳间隔常设为 5ms,容易触发假断连;可临时调大health_check_interval=15观察 - 用
docker network inspect bridge查看是否启用了com.docker.network.bridge.enable_ip_masquerade,开启状态会引入额外 NAT,干扰SUBSCRIBE的 TCP 长连接稳定性
真正卡住的地方往往不在代码逻辑,而在 Docker 网络命名空间和 Redis 连接目标之间的错位——SUBSCRIBE 成功但 NUMSUB 为 0,或 --monitor 看到 PUBLISH 却没进 listen(),基本都是这一层出了问题。











