7
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  // In this case it is an HTTP server



  var sticky = require('sticky-session');
  var express = require('express');
  var app = express();

  app.get('/', function (req, res) {
      console.log('worker: ' + cluster.worker.id);
      res.send('Hello World!');
  });


  var server = http.createServer(app);
      sticky.listen(server,3000);

  console.log(`Worker ${process.pid} started`);
}

我查阅了有关 nodejs clustering and sticky-session 的文档以及关于此的另一个堆栈溢出答案

  var cluster = require('cluster');
  var http = require('http');
  var sticky = require('sticky-session');
  var express = require('express');
  var app = express();

  app.get('/', function (req, res) {
      console.log('worker: ' + cluster.worker.id);
      res.send('Hello World!');
  });


  var server = http.createServer(app);
      sticky.listen(server,3000);

如果上面的代码片段在没有分叉的情况下运行,它可以正常工作,但如上面的集群示例所示,它永远不会工作,其中线程已启动但服务器从未初始化。

我读到有粘性集群的替代方案,有人可以就这个主题给出一个适当的权威答案,这对寻找相同的人很有用,另一个主要问题是 app.locals 对象,它用于存储变量一个应用程序实例和多个服务器实例的出现导致此中断,因为不同实例的值会有所不同,因此这种方法会导致一个大问题并且应用程序会中断。回答时请不要复制粘贴一些代码,请提供详细的答案,详细说明接近它的好处和缺点。

我不是在寻找仅限于使用sticky-sessions nodejs模块的答案,我欢迎使用处理器的所有内核并确保会话连续性的所有其他方法。

如果它涉及 RedisStore 或 MongoDb 存储就可以了,我想知道的是关于具有会话连续性的集群的 nodejs 应用程序的标准方法

https://github.com/indutny/sticky-session

https://nodejs.org/api/cluster.html

https://stackoverflow.com/a/37769107/3127499

4

3 回答 3

3

您的代码中有一个小问题。“ sticky-session ”模块已经使用了node.js的“ cluster ”模块。你不需要“ fork() ”因为sticky-session已经为你做了。让我们看看如何:

var cluster = require('cluster'); // Only required if you want the worker id
var sticky = require('sticky-session');

var server = require('http').createServer(function(req, res) {
  res.end('worker: ' + cluster.worker.id);
});
sticky.listen(server, 3000);

调用sticky.listen() 已经为你生成了worker。参见下面的listen() 实现

 function listen(server, port, options) {
  if (!options)
    options = {};

  if (cluster.isMaster) {
    var workerCount = options.workers || os.cpus().length;

    var master = new Master(workerCount, options.env);
    master.listen(port);
    master.once('listening', function() {
      server.emit('listening');
    });
    return false;
  }
  return true;
}

这一行 var master = new Master(workerCount, options.env) 负责生成工人。请参阅下面的 Master() 实现:

function Master(workerCount, env) {
  net.Server.call(this, {
    pauseOnConnect: true
  }, this.balance);

  this.env = env || {};

  this.seed = (Math.random() * 0xffffffff) | 0;
  this.workers = [];

  debug('master seed=%d', this.seed);

  this.once('listening', function() {
    debug('master listening on %j', this.address());

    for (var i = 0; i < workerCount; i++)
      // spawning workers
      this.spawnWorker();
  });
}

因此,当您调用sticky.listen(server,port) 时,您实际上是在调用cluster.fork()。因此您不应该再次显式调用fork()。现在您的代码应如下所示:

var cluster = require('cluster'); // Only required if you want the worker id
var sticky = require('sticky-session');

var server = require('http').createServer(function(req, res) {
  res.end('worker: ' + cluster.worker.id);
});

//sticky.listen() will return false if Master
if (!sticky.listen(server, 3000)) { 
  // Master code
  server.once('listening', function() {
    console.log('server started on 3000 port');
  });
} else {
  // Worker code
}

要记住的一件重要事情是,生成的工人将有自己的EVENTLOOP内存,因此资源不会相互共享。您可以使用“REDIS”或其他 npm 模块(例如“memored”)在不同的工作人员之间共享资源。

希望这能解决你的两个问题。

于 2018-07-19T09:07:18.703 回答
2

我认为您将粘性会话共享内存存储混淆了。

让我试着帮忙:

  • 粘性会话模块正在使用它们的 IP 地址平衡请求。因此客户端将始终连接到同一个工作服务器,并且 socket.io 将按预期工作,但在多个进程上!

实施粘性会话意味着您现在有多个节点接受连接。但是,它保证这些节点将共享相同的内存,因为每个工作人员都有自己的事件循环和内部内存状态。

换句话说,一个节点正在处理的数据可能对其他工作节点不可用,这解释了您指出的问题。

...另一个主要问题是 app.locals 对象,该对象用于存储应用程序实例的变量,并且多个服务器实例的出现导致此中断,因为不同实例的值将不同,因此这种方法会导致一个大问题和应用程序中断...

因此,要解决这个问题,我们需要使用Redis之类的东西,以便可以在多个节点之间共享数据

希望这可以帮助!

于 2019-09-22T06:06:52.703 回答
0

如果我正确理解您的问题,您正在处理内存数据存储或会话存储。这是多节点或集群中基于会话的身份验证中的已知问题之一。假设您调用了节点 A 并获得了名为 sessionA 的会话,但在下一次调用中,您将其调用到了节点 B。节点 B 对 sessionA 一无所知。人们试图通过使用粘性会话来解决这个问题,但这还不够。好的做法是使用替代方法,例如 JWT 或 oAuth2。我更喜欢 JWT 进行服务到服务的通信。JWT 不存储任何东西并且是无状态的。由于 REST 也是无状态的,因此它与 REST 配合得非常出色。这里https://www.rfc-editor.org/rfc/rfc7519是 JWT 实现的规范。如果您需要某种刷新令牌,那么您需要考虑存储。存储可以是 REDIS、MongoDB 或任何其他基于 SQL 的数据库。有关 nodejs 中 JWT 的进一步说明:

https://jwt.io/

https://jwt.io/introduction/

https://www.npmjs.com/package/jsonwebtoken

https://cloud.google.com/iot/docs/how-tos/credentials/jwts#iot-core-jwt-refresh-nodejs

于 2018-07-24T15:15:01.990 回答