可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm doing a little OJT on my first node project and, while I can stand up a simple server, the app is going to get hammered so using cluster seems like a good idea. I've cobbled together some code snippets that I've found in various searches (including SO), but the server won't start. I'm sure my inexperience with node has me doing something stupid, but I don't see it.
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 ); }
RESULT:
TypeError: Object #<Cluster> has no method 'repl'
If I remove the use
calls, the workers start up correctly, but process.env.NODE_WORKER_ID
is undefined
. Inspecting process.env
shows me that it's definitely not defined. Maybe the snippet I used was from an old version, but I'm not sure how to identify the worker thread in any other way.
If anyone can unscrambled whatever I've scrambled, I'd really appreciate it.
回答1:
For anyone searching later, here's what I ended up with:
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 ); }
I'm still very early in the node learning curve, but the server starts and appears to have a working running on each core. Thanks to JohnnyH for getting me on the right track.
回答2:
Also take a look at cluster2. It's used by eBay and has an express example
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); });
回答3:
Here is my draft of Cluster.js class. Note that we should catch port conflict when you start master process.
/*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 }; };