NestJS中Socket.io Redis Adapter无法触发Gateway事件的问题
解决NestJS Socket.io Redis Adapter触发@SubscribeMessage事件的问题
核心原因
直接向Redis发布原始字符串(比如'test')不符合Socket.io Redis Adapter要求的消息结构。Socket.io的Redis适配器会将消息封装为包含type、nsp、data等字段的特定JSON格式,只有符合该格式的消息,才能被Gateway的@SubscribeMessage正确识别并触发对应方法。
具体解决方案
1. 按Socket.io规范构造Redis消息
在另一个NestJS应用中,不要直接发布原始字符串,而是构造符合Socket.io要求的JSON结构后再发布:
// 另一个NestJS应用的Redis发布代码示例 await redisClient.publish( 'socket.io#/#', // 频道格式规则:socket.io#<命名空间>#,默认命名空间为/,所以是socket.io#/# JSON.stringify({ type: 'event', // 固定值为event nsp: '/', // 与Gateway使用的命名空间保持一致,默认是/ data: ['test', '你的消息内容'], // 第一个元素是事件名,后续元素为事件参数 id: '1', // 可选字段,消息ID,可省略 }) );
2. 匹配Gateway的命名空间配置
如果你的EventsGateway设置了自定义命名空间,比如:
@WebSocketGateway({ namespace: '/chat' }) export class EventsGateway { @SubscribeMessage('test') handleTest(client: Socket, payload: any) { // 业务处理逻辑 } }
那么Redis发布的频道需要改为socket.io#/chat#,同时消息中的nsp字段也要设为/chat。
3. 确保Redis Adapter初始化正确
检查RedisIoAdapter和main.ts的配置,保证Redis连接与适配器挂载逻辑正确:
// RedisIoAdapter示例代码 export class RedisIoAdapter extends IoAdapter { private redisAdapter: any; async connectToRedis() { const pubClient = createClient({ url: 'redis://localhost:6379' }); const subClient = pubClient.duplicate(); await Promise.all([pubClient.connect(), subClient.connect()]); this.redisAdapter = createAdapter(pubClient, subClient); } createIOServer(port: number, options?: ServerOptions) { const server = super.createIOServer(port, options); server.adapter(this.redisAdapter); return server; } } // main.ts中的初始化逻辑 async function bootstrap() { const app = await NestFactory.create(AppModule); const redisIoAdapter = new RedisIoAdapter(app); await redisIoAdapter.connectToRedis(); app.useWebSocketAdapter(redisIoAdapter); await app.listen(3000); } bootstrap();
4. 移除手动订阅Redis频道的逻辑
不要在connectToRedis方法中手动订阅Redis频道,Socket.io的Redis适配器会自动处理频道订阅与消息转发,手动订阅会干扰适配器的正常工作流程。
测试验证
- 启动两个NestJS应用,确保Redis服务正常运行。
- 在另一个应用中按照正确格式发布消息。
- 检查
EventsGateway中@SubscribeMessage('test')修饰的方法是否被触发。
内容的提问来源于stack exchange,提问作者TheZerbibi
相关产品推荐
相关产品推荐

