为什么Node cluster.fork()在作为模块实现时要分叉父作用域?



我试图实现一个使用cluster的节点模块。问题是整个父范围与预期的集群代码一起分叉。我是在用Mocha为模块编写测试时发现的:测试套件将运行多次,而不是一次。

见下面,myModule.js创建了N个worker,每个CPU一个。这些工作器是http服务器,也可以是其他任何东西。

每次test.js运行时,脚本运行N + 1次。在下面的例子中,console.log在我的四核上运行了5次。

有人能解释一下这是一个实现问题还是集群配置问题?是否有任何方法可以限制fork()的作用域,而不必导入模块(如此解决方案https://github.com/mochajs/mocha/issues/826)?

/// myModule.js ////////////////////////////////////
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
var startCluster = function(){
  if (cluster.isMaster) {
    // CREATE A CLUSTER OF FORKED WORKERS, ONE PER CPU
    //master does not listen to UDP messages.
    for (var i = 0; i < numCPUs; i++) {
      var worker = cluster.fork();
    }

  } else {
    // Worker processes have an http server.
    http.Server(function (req, res){
      res.writeHead(200);
      res.end('hello worldn');
    }).listen(8000);
  }
  return
}
module.exports = startCluster;
/////////////////////////////////////////////////
//// test.js ////////////////////////////////////
var startCluster = require('./myModule.js')
startCluster()
console.log('hello');
////////////////////////////////////////////////////////

我来大胆回答一下。仔细查看节点文档,有一个cluster.setupMaster可以覆盖默认值。cluster.fork()的默认值是执行当前脚本,带有"文件路径到工作文件"。(默认= process.argv[1])"https://nodejs.org/docs/latest/api/cluster.html cluster_cluster_settings

因此,如果另一个模块正在导入带有cluster.fork()调用的脚本,它仍然会使用process的路径。Argv[1],这可能不是您期望的路径,并且会产生意想不到的后果。

所以我们不应该像官方文档建议的那样在同一个文件中初始化集群主节点和工作节点。谨慎的做法是将worker分离到一个新文件中,并覆盖默认设置。(同样为了安全起见,您可以添加目录路径__dirname)。

cluster.setupMaster({ exec: __dirname + '/worker.js',});

下面是正确的实现:

/// myModule.js ////////////////////////////////////
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
var startCluster = function(){
  cluster.setupMaster({ 
    exec: __dirname + '/worker.js'
  });
  if (cluster.isMaster) {
    // CREATE A CLUSTER OF FORKED WORKERS, ONE PER CPU    
    for (var i = 0; i < numCPUs; i++) {
       var worker = cluster.fork();
    }
  } 
  return
}
module.exports = startCluster;
/////////////////////////////////////////////////
//// worker.js ////////////////////////////////////
var http = require('http');
// All worker processes have an http server.
http.Server(function (req, res){
res.writeHead(200);
res.end('hello worldn');
}).listen(8000);
////////////////////////////////////////////////////////
//// test.js ////////////////////////////////////
var startCluster = require('./myModule.js')
startCluster()
console.log('hello');
////////////////////////////////////////////////////////

你应该只看到一次'hello'而不是1 * cpu数量

你需要让"isMaster"的东西在你的代码的顶部,而不是在函数内。worker将从模块的顶部运行(它不像c++的fork, worker从fork()点开始)。

我假设你想startCluster = require('./cluster-bug.js')只eval一次?这是因为整个脚本是集群运行的。在startCluster中指定的只是使它在主集群和从集群之间变化。

最新更新