Использование кластера в приложении Expressjs

Я делаю небольшой OJT в своем первом проекте узла, и, хотя я могу встать на простом сервере, приложение будет забито, поэтому использование кластера кажется хорошей идеей. Я собрал воедино несколько фрагментов кода, которые я нашел в различных поисках (включая SO), но сервер не запускается. Я уверен, что моя неопытность с узлом заставляет меня делать глупости, но я этого не вижу.

var express = require( 'express' );
var cluster = require( 'cluster' );
var path    = require( 'path' );

var cCPUs   = require( 'os' ).cpus().length;
var port    = 3000;
var root    = path.dirname( __dirname );

if( cluster.isMaster ) {
    for( var i = 0; i < cCPUs; i++ ) {
      cluster.fork();
    }

    cluster.on( 'death', function( worker ) {
      console.log( 'Worker ' + worker.pid + ' died.' );
    });
}
else {
    // eyes.inspect( process.env );
    console.log( 'Worker: %s', process.env.NODE_WORKER_ID );

    var app = express();
    var routes  = require( './routes' )( app );
    app
      .use( cluster.repl( root + 'cluster.repl' ) )
      .use( cluster.stats({ connections: true, requests: true }) )
      .use( cluster.reload( root ) )
      .listen( port );
}

РЕЗУЛЬТАТ:

TypeError: Object #<Cluster> has no method 'repl'

Если я удалю use звонки, рабочие запускаются правильно, но process.env.NODE_WORKER_ID является undefined, инспектирование process.env показывает мне, что это точно не определено. Возможно, фрагмент, который я использовал, был из старой версии, но я не уверен, как идентифицировать рабочий поток каким-либо другим способом.

Если кто-нибудь сможет расшифровать то, что я сделал, я был бы очень благодарен.

3 ответа

Решение

Для тех, кто ищет позже, вот что я закончил:

var cluster = require( 'cluster' );
var express = require( 'express' );
var path    = require( 'path' );

var port    = 3000;
var root    = path.dirname( __dirname );
var cCPUs   = require('os').cpus().length;

if( cluster.isMaster ) {
  // Create a worker for each CPU
  for( var i = 0; i < cCPUs; 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 + ' died.' );
  });
}
else {
  var app    = express();
  var routes = require( './routes' )( app );

  app
    .use( express.bodyParser() )
    .listen( port );
}

Я все еще очень рано на кривой обучения узлов, но сервер запускается и, похоже, работает на каждом ядре. Спасибо JohnnyH за то, что поставили меня на правильный путь.

Также взгляните на cluster2. Он используется eBay и имеет яркий пример

var Cluster = require('cluster2'),
    express = require('express');

var app = express.createServer();

app.get('/', function(req, res) {
  res.send('hello');
});

var c = new Cluster({
  port: 3000,
});

c.listen(function(cb) {
  cb(app);
});

Вот мой проект класса Cluster.js. Обратите внимание, что мы должны перехватить конфликт портов при запуске главного процесса.

/*jslint indent: 2, node: true, nomen: true, vars: true */

'use strict';

module.exports = function Cluster(options, resources, logger) {
  var start = function () {
    var cluster = require('cluster');

    if (cluster.isMaster) {
      require('portscanner').checkPortStatus(options.express.port, '127.0.0.1', function (error, status) {
        if (status === 'open') {
          logger.log.error('Master server failed to start on port %d due to port conflict', options.express.port);
          process.exit(1);
        }
      });

      // Each core to run a single process.
      // Running more than one process in a core does not add to the performance.
      require('os').cpus().forEach(function () {
        cluster.fork();
      });

      cluster.on('exit', function (worker, code, signal) {
        logger.log.warn('Worker server died (ID: %d, PID: %d)', worker.id, worker.process.pid);
        cluster.fork();
      });
    } else if (cluster.isWorker) {
      var _ = require('underscore');
      var express = require('express');
      var resource = require('express-resource');

      // Init App

      var app = express();

      // App Property

      app.set('port', process.env.PORT || options.express.port);
      app.set('views', options.viewPath);
      app.set('view engine', 'jade');
      app.set('case sensitive routing', true);
      app.set('strict routing', false);

      // App Middleware

      app.use(express.favicon(options.faviconPath));
      app.use(express.logger({ stream: logger.stream() }));
      app.use(express.bodyParser());
      app.use(express.methodOverride());
      app.use(express.responseTime());
      app.use(app.router);
      app.use(require('stylus').middleware(options.publicPath));
      app.use(express['static'](options.publicPath));

      if (options.express.displayError) {
        app.use(express.errorHandler());
      }

      // App Format

      app.locals.pretty = options.express.prettyHTML;

      // App Route Handler

      if (!_.isUndefined(resources) && _.isArray(resources)) {
        _.each(resources, function (item) {
          if (!_.isUndefined(item.name) && !_.isUndefined(item.path)) {
            app.resource(item.name, require(item.path));
          }
        });
      }

      // Start Server

      var domain = require('domain').create();

      domain.run(function () {
        require('http').createServer(app).listen(app.get('port'), function () {
          logger.log.info('Worker server started on port %d (ID: %d, PID: %d)', app.get('port'), cluster.worker.id, cluster.worker.process.pid);
        });
      });

      domain.on('error', function (error) {
        logger.log.error(error.stack);
      });
    }
  };

  return {
    start: start
  };
};
Другие вопросы по тегам