Node.js集群、Redis、单线程和非阻塞I/O如何工作?

4

我将总结一下自己最近学习的有关节点配置的知识。如果正确,请确认。如果出错,请纠正我和其他人。

Node.js是一个服务器。 作为服务器,它可以接收来自客户端的请求。 假设在一段时间内有200个请求。

由于node.js是单线程的,因此可以通过一个单元/一个核心处理所有这些请求。

但是,node也是事件非阻塞的I/O,这意味着某人可以请求一些需要1分钟才能完成的node.js API方法,而其他人也可以请求另一个需要1分钟才能完成的方法。这两者都可以同时处理,无需等待彼此完成。

现在听起来很棒,但如果我们协助node服务器,并让它作为代理将那200个请求的任务分配给集群/工作进程,那么它甚至可以变得更好。因此,node服务器成为客户端与工作进程之间的代理。就像一个项目经理:)

这样做的好处是,我们现在有4个核心处理这些请求,而不是一个核心处理200个请求,每个核心都是事件非阻塞的I/O(当然取决于服务器规格)。

那肯定超级快。不是吗?

这里存在一个问题/我想要理解的问题:

如何使这些集群/工作进程共享工作?

我的意思是,如果有50个请求传输到核心1,另外50个请求传输到核心2,以此类推,直到所有可用核心,那么我该如何将socket emit发送到两个不同的核心? (socket.io)

集群如何与速率限制器一起运行?我的意思是,速率限制器会阻止尝试垃圾邮件等操作的用户,但然后立即撤销这个用户的禁令,因为它进入了不同的集群。

我听说Redis应该能够帮助解决这个问题。 但很混乱,redis是一个数据库,不是吗? 我正在使用MongoDB,所以为什么需要更多的数据库呢?

我在这里放一些代码,也许解决方案就在这个角落里:

var cluster = require('cluster');
var redis = require("socket.io-redis");

if(cluster.isMaster) {

    var numWorkers = require('os').cpus().length;
    console.log('Master cluster setting up ' + numWorkers + ' workers...');

    for(var i = 0; i < numWorkers; i++) {
        cluster.fork();
    }

    cluster.on('online', function(worker) {
        console.log('Worker ' + worker.process.pid + ' is online');
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('Worker ' + worker.process.pid + ' stopped with code: ' + code + ', and signal: ' + signal);
        console.log('Starting a new worker');
        cluster.fork();
    });

    cluster.fork();

} else {

  // define main required variables
  var express = require('express');
  var app = express();
  var cors = require('cors');
  var server = require('http').createServer(app);
  var io = require('socket.io')(server);
  var mongojs = require('mongojs');
  var db = mongojs("mongodb://user:pass*@ip:port/dbname", []);
  var ObjectId = require('mongodb').ObjectID;
  var CronJob = require('cron').CronJob;
  var request = require('request');
  var fcm = require('./routes/fcm');
  var Excel = require('exceljs');
  var fs = require('fs');
  var path = require('path');
  var rateLimit = require('express-rate-limit');

  // define some global limiter against spammer
  var limiter = new rateLimit({
    windowMs: 10*60*1000, // 10 minutes
    max: 100, // limit each IP to 100 requests per windowMs (11 is on load and more 100 later on)
    delayMs: 0, // disable delaying - full speed until the max limit is reached
    message: "Service is block for you, try again later"
  });

  //  apply to all requests
  app.use(limiter);

  // allow cross origin to access my api
  app.use(cors());

  var bodyParser = require('body-parser');
  app.use(bodyParser.json()); // support json encoded bodies
  app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
  app.use(express.static(__dirname + '/src'));

  // my own routes
  require('./routes/sockets')(app, io, mongojs, db, ObjectId, CronJob, request, fcm);
  require('./routes/userServer')(app, io, mongojs, db, ObjectId, CronJob, request, fcm, rateLimit);
  require('./routes/ridesServer')(app, io, mongojs, db, ObjectId, CronJob, request);
  require('./routes/offersServer')(app, io, mongojs, db, ObjectId, CronJob, request, rateLimit);
  require('./routes/notificationsServer')(app, io, mongojs, db, ObjectId, CronJob, request);
  require('./routes/scopeServer')(app, io, mongojs, db, ObjectId, CronJob, request, fcm);
  require('./routes/excelServer')(app, io, mongojs, db, ObjectId, CronJob, request, fcm, Excel, fs, path);

  // listen
  server.listen("8080", function() {
    console.log("Connected to db and listening on port 8080");
  });

}

如何处理这些事情?

谢谢。


在这种情况下,Redis只是帮助您维护集群节点之间共享的状态,理论上您也可以使用MongoDB来实现,但Redis可能会更适合这个特定任务的扩展,并且许多集群管理工具已经支持它。对于socket emit,您真的需要将其发送到两个核心吗?因为我猜您不希望emit操作触发两次动作,如果它到达其中一个核心就足够了。 - Qiong Wu
我不确定是否需要将其发送到所有核心,我只需要设置它工作并设置所有必要的侦听器,例如:插入新帖子时,我需要将其发射给所有查看板的人。 但是据我所知,核心1中有50个用户,核心2中也有50个用户。 在核心1中的用户不会听取核心2中的内容,我错了吗? - Raz
请问您能否根据我的代码提供一个示例,使其只向所有监听器发送一次发射,并正确维护速率限制? - Raz
啊,我现在更清楚地理解了。在这种情况下,添加Redis是有意义的,因为您可以使用PUB/SUB将事件传播到核心。不幸的是,我现在没有足够的资源来提供完整的示例,但是https://github.com/NodeRedis/node_redis#publish--subscribe应该能够帮助您。 - Qiong Wu
@QiongWu请检查我的答案,我已经找到了解决方案。 - Raz
1个回答

2

我已经找到了解决方案,以下是具体步骤:

如果要使用socket.io多个集群,请按照以下步骤操作:

第一步:在您的计算机上安装Redis:brew install redis

第二步:运行Redis服务器:redis-server(从SHELL的新选项卡中运行)。

第三步:安装Redis依赖项:npm install redis --save

第四步:io定义之后包含Redis适配器,如下所示:

var io = require('socket.io')(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

你已经准备好了。

如果要使用Redis进行速率限制,请按照下一个NPM Repo的说明操作。


1
感谢分享! - Qiong Wu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接