Using cluster in an Expressjs app

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.

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.

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);
});

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
  };
};