Использование кластера в приложении 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
};
};