PM2集群下socket.io-redis-adapter的fetchSocket()问题及Socket与RemoteSocket区别
Socket与RemoteSocket的区别
- Socket实例:是当前进程中真实存在的客户端连接对象,包含完整的连接上下文(如
client、conn)以及你自定义的eventMetaData属性,支持直接调用所有Socket原生方法(emit、disconnect等),也能通过socketClient.sockets.sockets.get(socketId)获取到——因为它本身就存储在当前进程的Socket映射表中。 - RemoteSocket实例:是跨进程的Socket代理对象,代表连接在其他PM2集群进程上的客户端。它只包含基础元数据(
id、handshake、rooms),没有实际的连接上下文和自定义属性,无法直接调用依赖本地连接的方法,仅能通过广播操作间接与客户端通信。所以用socketClient.sockets.sockets.get(socketId)会返回null,因为目标Socket并不在当前进程中。
PM2集群模式下的解决方案
在集群模式下,Socket.io依赖Redis适配器实现跨进程通信,每个进程仅持有自身处理的Socket连接,无法直接获取其他进程的真实Socket实例。针对你的需求,可以采用以下方案:
1. 将自定义数据存入共享存储
不要把eventMetaData直接挂载在Socket实例上,而是存入Redis(可复用Socket.io的Redis适配器实例)或其他共享存储中,以Socket的id作为键:
// 客户端连接时存储自定义数据 io.on('connection', async (socket) => { const eventMetaData = { userObjectId: '12345' }; await redisClient.set(`socket:${socket.id}:meta`, JSON.stringify(eventMetaData)); // 断开连接时清理数据 socket.on('disconnect', async () => { await redisClient.del(`socket:${socket.id}:meta`); }); }); // 获取Socket时从共享存储读取数据 const sockets = await io.in("room1").fetchSockets(); for (const socket of sockets) { const metaStr = await redisClient.get(`socket:${socket.id}:meta`); socket.eventMetaData = metaStr ? JSON.parse(metaStr) : {}; }
2. 通过跨进程请求获取远程Socket数据
利用Socket.io的serverSideEmit能力,向持有目标Socket的进程发送请求,获取自定义数据:
// 发起跨进程请求获取元数据 io.serverSideEmit('get-socket-meta', targetSocketId, (response) => { console.log('获取到的元数据:', response.eventMetaData); }); // 每个进程监听并处理请求 io.on('get-socket-meta', (socketId, callback) => { const localSocket = io.sockets.sockets.get(socketId); if (localSocket) { callback({ eventMetaData: localSocket.eventMetaData }); } else { callback({ eventMetaData: {} }); } });
3. 优先使用RemoteSocket的广播能力
如果仅需要与客户端通信,RemoteSocket已经支持emit等广播方法,无需强制获取真实Socket实例。只有当必须访问本地连接上下文或自定义属性时,再使用前两种方案。
内容的提问来源于stack exchange,提问作者s__k
相关产品推荐
相关产品推荐

