Node.js Redis NRP订阅回调未执行:API网关收不到返回消息
我来帮你一步步拆解这个问题——你已经打通了API网关到通知服务的发布链路,Redis也显示网关已经订阅了目标频道,但就是收不到返回消息的回调,大概率是几个细节没处理到位,咱们逐个排查:
1. 先确认:回调到底有没有被触发?
你现在的回调里加了data.rmid == data.mid的判断,很可能消息已经收到了,但因为ID不匹配没走到resolve逻辑,导致你误以为回调没触发。
先给订阅回调加个全局日志,把所有收到的消息都打出来:
Subscribe(pattern, callback) { console.log('Im gonna subscribe to', pattern); nrp.on(pattern, (data, channel) => { // 新增这行,不管ID匹配与否,先打印所有收到的消息 console.log('[DEBUG] 收到频道消息:', channel, data); console.log('subd on ' + pattern); return callback(data, channel); }); }
同时在API网关的订阅回调里也加日志:
this.sub.Subscribe(subchannel, (data, channel) => { console.log('[API网关] 收到回调数据:', data); let body = data.body; let message = body.message; if(data.rmid == data.mid){ // ... 原有逻辑 } });
跑一遍登录流程,看控制台有没有打印这些日志——如果有,说明回调已经触发,问题出在ID匹配逻辑上;如果没有,再往下排查。
2. 检查NRP订阅方法的实现是否符合库的规范
从你的代码看,你直接用nrp.on(pattern, ...)来绑定订阅回调,但很多Redis Pub/Sub库中,on是用来监听全局事件(比如message),而不是直接订阅频道。如果NRP的API要求先调用subscribe方法再监听消息,那你的写法就错了。
比如NRP的正确订阅方式可能是这样:
Subscribe(pattern, callback) { console.log('Im gonna subscribe to', pattern); // 先调用订阅API,订阅目标频道 nrp.subscribe(pattern); // 监听全局的message事件,判断频道是否匹配 nrp.on('message', (channel, data) => { if (channel === pattern) { console.log('subd on ' + pattern); callback(data, channel); } }); }
你可以对照NRP的官方文档确认API用法——如果当前的Subscribe方法没有主动调用订阅接口,那网关其实只是绑定了事件,并没有真正订阅频道(虽然Redis客户端显示已订阅,但可能是其他逻辑导致的)。
3. 验证消息ID的匹配逻辑
你的回调逻辑依赖data.rmid == data.mid,但要确保:
- API网关发布消息时,有没有生成唯一的
mid并传递给通知服务?比如发布时要带上这个ID:// 生成唯一消息ID const mid = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; const sent = this.sub.publish(pubchannel, { mid: mid, // 把mid放到根字段,方便通知服务获取 body: { param: params, message: '', } }); - 通知服务处理完登录后,有没有把收到的
mid作为rmid返回?比如通知服务发布返回消息时:// 假设收到的消息是reqData nrp.publish(subchannel, { rmid: reqData.mid, // 用网关发来的mid作为rmid返回 body: { message: '登录成功' } });
如果这两个ID不对应,你的回调里的逻辑就永远不会执行。
4. 检查退订和关闭连接的时机
你在回调里直接调用sub.unSubscribe((nrp)=>{ nrp.quit(); resolve(message); });,这个写法可能有问题:
- 先确保
unSubscribe方法能正确退订指定频道,而不是退订所有频道; - 建议先
resolve结果,再执行退订和关闭连接的操作,避免因为连接提前关闭导致resolve逻辑没完成:if(data.rmid == data.mid){ // 先返回结果 resolve(message); // 再退订频道并关闭连接 sub.unSubscribe(subchannel); nrp.quit(); }
最后再确认一个小细节
确保API网关用来发布和订阅的是同一个NRP实例——如果发布用了一个实例,订阅用了另一个,哪怕Redis显示订阅了频道,不同实例的连接也收不到消息。
内容的提问来源于stack exchange,提问作者Gisrou8

