You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在app.js外部复用Socket.IO实例供控制器直接导入使用

Socket.IO跨文件复用解决方案

针对三层架构下Socket.IO实例重复初始化、无法跨文件调用的问题,通过单例模式抽离独立模块即可解决,实现一次初始化全项目复用:


1. 抽离独立Socket.IO单例模块

新建utils/socket.js文件,统一管理实例的初始化和获取逻辑,保证全局只有一个IO实例:

// utils/socket.js
let ioInstance;

module.exports = {
  // 服务启动时调用一次,完成实例初始化和全局事件绑定
  init: (httpServer) => {
    const { Server } = require('socket.io');
    ioInstance = new Server(httpServer, {
      cors: {
        origin: "*", // 按需修改跨域配置
        methods: ["GET", "POST"]
      }
    });

    // 全局命名空间、连接监听统一在这里绑定,不要散落在控制器中
    ioInstance.of('/not').on("connection", (socket) => {
      console.log("/not namespace connected, socket id:", socket.id);
      // 这里可以统一写鉴权、通用事件监听逻辑
    });

    return ioInstance;
  },
  // 业务层调用该方法获取已初始化的实例
  getIO: () => {
    if (!ioInstance) throw new Error("Socket.IO未初始化,请先在服务入口执行init方法");
    return ioInstance;
  }
}

2. 服务入口统一初始化实例

在项目启动文件(app.js/bin/www等HTTP服务初始化位置),完成IO实例的一次性初始化:

const express = require('express');
const app = express();
const http = require('http');
// 基于express服务创建HTTP server,供Socket.IO挂载
const server = http.createServer(app);

// 导入socket模块,执行全局初始化
const { init: initSocket } = require('./utils/socket');
initSocket(server);

// 后续正常注册中间件、路由即可
const userRoutes = require('./routes/user');
app.use('/api/user', userRoutes);

server.listen(3000, () => {
  console.log('服务启动在3000端口');
});

3. 控制器层直接导入调用

控制器中不需要重复初始化IO、重复绑定连接事件,直接引入getIO方法即可拿到实例使用。修正后的getAllUserConnections代码如下:

// 控制器文件顶部导入工具方法
const { getIO } = require('../utils/socket');

async getAllUserConnections (req,res){
  const uid = req.user.id;
  try {
    const data = await db.userConnections.findAll({
      where: {
        resiver_user_id: uid
      },
      include: [{
        model: db.user
      }]
    });

    // 需要推送消息时直接获取实例调用即可
    const io = getIO();
    // 示例:向/not命名空间下的指定用户推送消息
    // io.of('/not').to(`user_${uid}`).emit('connectionListUpdate', data);

    if (data.length === 0) return res.send('there is no Data to get');
    return res.send(data);
  } catch(error) {
    return res.send(error);
  }
}

关键注意事项

  • 所有connection事件监听、命名空间配置必须统一在init方法中绑定,绝对不要写在接口处理函数内,否则每次接口被请求都会重复绑定事件,引发内存泄漏、事件重复触发的问题。
  • 必须保证Socket.IO初始化逻辑在路由注册、服务启动监听之前执行,避免业务层调用时实例还未创建。

内容的提问来源于stack exchange,提问作者Mouayad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 00:24:31