【问题标题】:Should I use a global variable to share socket.io instance across entire server我应该使用全局变量在整个服务器上共享 socket.io 实例吗
【发布时间】:2018-10-28 10:58:44
【问题描述】:

以下是我的 node.js 应用程序中的 server.js 文件。我希望服务器上的其他文件可以访问我的 socket.io 实例,以便从我的 API (listingRoutesApi, userRoutesApi etc.) (refer to code) 发出事件。

我的问题是我的路由是在创建服务器之前声明的;但是,socket.io 实例是在服务器创建后创建的。

我使用的解决方案是声明一个全局 io 变量,这将允许我从我的网络应用程序中的任何位置发出事件,如下所示:

global.io.of('/analytics').to(listing._id).emit('message', "There was a post.");

我的问题是:这样做有什么陷阱/危险吗?从长远来看,我会遇到任何可扩展性问题吗?另外,有没有更好的方法来实现我的目标?

我的 server.js 文件中的代码:

const app = express();    

app.use('/api', listingRoutesApi);
app.use('/api', userRoutesApi);
app.use('/api', imageRoutesApi);
// ...plenty more endpoints here...

app.use(serveStatic(path.join(__dirname, "/dist")));
app.use(history());
app.use(serveStatic(path.join(__dirname, "/dist")));

const server = app.listen(port, () => { console.log('server started ' + port); });

/* Start socket. */
global.io = socketio(server);

const analytics = global.io.of("/analytics");

analytics.on('connection', (socket) => {
    socket.on('join', (data) => {
        socket.join(data.room);
        analytics.in(data.room).emit('message', `New user joined ${data.room}`);
    });
    socket.on('leave', (data) => {
        analytics.in(data.room).emit('message', `User leaving ${data.room}`);
        socket.leave(data.room);
    });
    socket.on('disconnect', () => {
        console.log('user disconnected');
    });
});

我问这个问题是因为this SO post 通过声明getIOInstance 函数并将其传递给所有需要它的模块来回答类似的问题。虽然它可以工作,但感觉不是很优雅,而且似乎有点不必要,因为我希望我的应用程序中只有一个 socket.io 实例。

此外,我认为我面临的挑战是一个非常普遍的挑战;但是,我找不到很多解决方案来解决它,也没有一个建议使用全局变量。

【问题讨论】:

  • @estus 你认为使用全局变量会遇到什么问题?
  • 尝试搜索“为什么全局变量在 js 中不好”。你会有一大堆问题。 Node.js 引入模块正是为了摆脱不适合的全局变量。

标签: node.js websocket socket.io


【解决方案1】:

Node.js 是一个模块化环境。模块应该解决全局变量的一些缺陷。

模块自然会提供单例实例,以防只需要一个实例:

app.js

module.export = express();

server.js

const app = require('./app');

// can go to app.js if configured but unlistened app is needed for reusability or testing
app.use(/* router */);

module.export = app.listen(...);

socketio.js

const server = require('./server');

module.export = socketio(server);

index.js

const app = require('./app');
const io = require('./socketio');
...

Express 还为应用程序全局依赖项提供了一个容器application settings table。它可以在任何应用程序实例可用的地方使用,例如在中间件中作为req.app.get(...)。如果它也是单例,那么在中间件之外访问 Express 应用程序实例也不会成为问题:

app.js

module.export = express();

index.js

const app = require('./app');

app.use(/* router */);
...

const server = app.listen(...);
const io = socketio(server);

app.set('io', io);

// available as req.app.get('io') inside middlewares
// and as require('./app').get('io') outside them

【讨论】:

  • 对不起,如果我在这里有点慢,但我不需要将 app.js 中的“服务器”传递给 socketio.js 以创建新的 socketio 实例吗?另外,假设这是真的,我是否还会遇到在创建服务器之前声明路由的初始问题,这意味着我将无法将 socketio.js 文件导入到@987654329 上面的任何模块中@?
  • 抱歉,忘记解释了。这是通过 Express 实例的单例解决的。这种模块化设置使测试更容易,而不是在一个文件中进行。如果需要,您可以模拟模块或监视它们。
  • 你所做的最后一次编辑,包括app.listen(...),让我的一切都变得更清楚了 :) 虽然,我必须说所有的 require 语句都让我头晕目眩,哈哈
  • 适应模块化方法需要一段时间。我添加了 Express 设置的说明。 Express 是惯用的,但不一定比单独使用模块来共享应用程序全局数据更好。
  • @JCraine 路由器在app = express() 之后但在app.listen 之前,因为它总是在 Express 应用程序中完成。我跳过了app.use,因为它们不会影响主题。避免循环依赖是答案的重点。如果您尝试通过从模块导入实例来访问io,则可以使用 CD。这就是get('io') 的用途。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
相关资源
最近更新 更多