ioredis重连后需手动重新订阅,因redis协议不维护订阅状态;应于ready事件中调用subscribe/psubscribe,分离pubclient与subclient,并在message回调中用try/catch捕获异常。

为什么subClient重连后收不到消息
ioredis自动重连成功后,subscribe 和 psubscribe 不会自动恢复——这是最常被忽略的硬性限制。Redis协议层不维护订阅状态,重连后连接是“干净”的,必须手动重新调用订阅方法。
常见现象:网络抖动后日志显示 ready 事件触发了,但 message 或 pmessage 回调始终不执行。
- 确认重连后是否在
ready事件中再次调用了subscribe('channel')(不是只在初始化时调一次) - 避免在
connect事件里订阅——ioredis v5+ 默认 lazy connect,connect可能早于真实建连完成 - 如果使用
psubscribe,注意通配符模式(如'user:*')也要在每次重连后重订
如何正确配置subClient的重连策略
默认重连策略对 Pub/Sub 场景不够健壮:指数退避可能让重连间隔过长,而短间隔密集重试又容易触发 Redis 的连接数限制。
推荐显式配置 retry_strategy,并结合业务容忍度控制节奏:
- 首次失败后立即重试(
return 0),避免冷启动延迟 - 重试次数超过 5 次后返回
false,防止无限循环(配合上层告警或降级) - 对
ETIMEDOUT或ECONNREFUSED错误可加固定偏移,避免雪崩
示例配置:
const subClient = new Redis({
host: 'redis.example.com',
retry_strategy: (times) => {
if (times === 0) return 0;
if (times > 5) return false;
return Math.min(times * 200, 1500) + Math.random() * 100;
}
});
pubClient 和 subClient 必须分离,不能复用
调用 subscribe 后,该连接进入 Redis 协议定义的「订阅模式」,此后所有非 SUBSCRIBE/UNSUBSCRIBE/PING/QUIT 命令都会被拒绝,并抛出 ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context 错误。
详细的 Three.js 3D 图形参考,涵盖场景设置、相机、几何体、材质、光照、动画、控制器、加载器、数学工具和调试。
这不是 ioredis 的 bug,而是 Redis 服务端强制行为。即使你用 duplicate() 或手动 new Redis() 创建新实例,只要底层 TCP 连接被 subscribe 占用,就不能再发 publish。
- 必须创建两个独立实例:
pubClient专用于publish,subClient专用于subscribe - 二者可共用相同配置(host/port/password),但不能共享连接对象或连接池
- 不要试图在
subClient上调用publish—— 即使它“看起来”连上了,也会在协议层被拦截
message回调里没捕获异常会导致静默丢消息
message 和 pmessage 回调是异步执行的,一旦内部抛出未捕获错误(比如 JSON.parse 失败、数据库写入异常),ioredis 不会重发该消息,也不会中断后续监听,整个流程就静默终止了。
必须在回调内做防御性包裹:
- 用
try/catch包住全部业务逻辑,至少记录错误日志 - 对关键消息(如订单状态变更)建议加简单重试机制(例如
setTimeout延迟 100ms 再处理) - 避免在回调里直接 await 长耗时操作;如有必要,应设超时并 fallback
示例:
subClient.on('message', (channel, message) => {
try {
const data = JSON.parse(message);
// 处理业务...
} catch (err) {
console.error('Failed to parse message on', channel, err);
// 可选:将原始消息存入 dead-letter queue
}
});
重连逻辑本身不难写,真正容易翻车的是——把重连当成“连上了就行”,却忘了订阅状态是应用层自己维护的契约。每一次 ready,都得像第一次那样重新 subscribe,否则消息永远进不来。










