Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 204e102215d971ff8f986af65605efc8fc1d8e80 @kriszyp committed Jun 15, 2010
Showing with 156 additions and 0 deletions.
  1. +50 −0 README.md
  2. +106 −0 lib/multi-node.js
@@ -0,0 +1,50 @@
+Multi-node provides launching of multiple NodeJS processes for TCP/HTTP serving.
+With multi-node it is very simple to add utilize multiple processes to concurrently
+serve HTTP requests, simply pass an http.Server object to the listen function:
+
+ var server = require("http").createServer(function(request, response){
+ ... standard node request handler ...
+ });
+ var nodes = require("multi-node").listen({
+ port: 80,
+ nodes: 4
+ }, server);
+
+The listen function takes two arguments, the first is the options, the second is the
+server. The options argument may have two properties, "port" (specifying the
+port number to listen on) and "nodes" (specifying the number of node processes).
+
+The object returned from the listen function also provides some useful capabilities.
+The return object has an isMaster property indicating if the current process is the
+original initiating master process. This can be used like:
+
+ var nodes = require("multi-node").listen(...);
+ if(nodes.isMaster){
+ // start a repl on just one process
+ require("repl").start();
+ }
+
+Multi-node also provides critical inter-process communication facilities. For any web
+application that requires processes to be able to communicate with each other
+(for sending messages like in chat applications, or for doing in-memory sessions, etc.),
+it is necessary for each process to be able to communicate with other processes.
+The returned object is also an event emitter, and the "node" event is fired for each
+other node process that is created. The event handler is a passed a readable and
+writable stream that can be used to communicate with the other process. For example:
+
+ var nodes = require("multi-node").listen(...);
+ var allStreams = [];
+ nodes.addListener("node", function(stream){
+ stream.addListener("data", function(data){
+ ... received a message from this node process ...
+ });
+ allStreams.push(stream);
+ });
+
+ function notifyOtherProcesses(message){
+ allStreams.forEach(function(stream){
+ stream.write(message);
+ });
+ }
+
+(Note that at this time, the stream is guaranteed to be immediately writable)
@@ -0,0 +1,106 @@
+var net = require("net"),
+ childProcess = require("child_process"),
+ lastStdout,
+ netBinding = process.binding("net");
+
+exports.ignoreReloadMessages = true;
+exports.listen = function(options, server){
+ var isMaster;
+ if(process.env._IS_CHILD_){
+ var stdin = new net.Stream(0, 'unix');
+ var descriptorType;
+ stdin.addListener('data', function(message){
+ descriptorType = message;
+ });
+ var siblingIn;
+ stdin.addListener('fd', function(fd){
+ if(descriptorType == "tcp"){
+ server.listenFD(fd, 'tcp4');
+ }
+/* else if(descriptorType == "sibling-start"){
+ var stream = new net.Stream(fd, "unix");
+ stream.resume();
+ stream.write("handshake");
+ siblingCallbacks.forEach(function(callback){
+ callback(stream);
+ });
+ }*/
+ else if(descriptorType == "sibling"){
+ var stream = new net.Stream(fd, "unix");
+ emitter.emit("node", stream);
+ stream.resume();
+ }
+ else{
+ throw new Error("Unknown file descriptor " + descriptorType);
+ }
+ });
+ stdin.resume();
+ }else{
+ isMaster = true;
+ var children = [],
+ tcpDescriptor = netBinding.socket("tcp4");
+ netBinding.bind(tcpDescriptor, options.port || 80);
+ netBinding.listen(tcpDescriptor, 128);
+ server.listenFD(tcpDescriptor, 'tcp4');
+ var priorArgs = process.argv;
+ if(process.platform == "cygwin" && priorArgs){
+ priorArgs = ["/usr/bin/bash","--login","-c", "cd " + process.cwd() + " && " + priorArgs.join(" ")];
+ }
+ var env = {_IS_CHILD_: "true"};
+ for(var i in process.env){
+ env[i] = process.env[i];
+ }
+ for(var i = 0; i < options.nodes - 1; i++){
+ var childConnection = netBinding.socketpair();
+ // spawn the child process
+ var child = children[i] = childProcess.spawn(
+ priorArgs[0],
+ priorArgs.slice(1),
+ env,
+ [childConnection[1], -1, -1]
+ );
+ child.master = new net.Stream(childConnection[0], 'unix');
+
+ child.master.write("tcp", "ascii", tcpDescriptor);
+ (function(child){
+ for(var j = 0; j < i; j++){
+ var siblingConnection = netBinding.socketpair();
+ /*var a = new net.Stream(siblingConnection[1], 'unix');
+ a.addListener('data', function(data){
+ a.pause();
+ child.master.write("sibling", "ascii", siblingConnection[1]);
+ });
+ a.resume();*/
+ child.master.write("sibling", "ascii", siblingConnection[1]);
+ children[j].master.write("sibling", "ascii", siblingConnection[0]);
+ }
+ var masterChildConnection = netBinding.socketpair();
+ process.nextTick(function(){
+ var stream = new net.Stream(masterChildConnection[0], "unix");
+ emitter.emit("node", stream);
+ stream.resume();
+ child.master.write("sibling", "ascii", masterChildConnection[1]);
+ });
+ })(child);
+
+ // Redirect stdout and stderr
+ child.stdout.addListener('data', function(data){
+ if(exports.ignoreReloadMessages && data.toString().substring(0, 10) == "Reloading "){
+ return;
+ }
+ process.stdout.write("\r" + data + "\r");
+ });
+ child.stderr.addListener('data', function(data){
+ require("sys").puts("\r" + data + "\r");
+ });
+ }
+
+ }
+ var emitter = new process.EventEmitter();
+ emitter.isMaster = isMaster;
+ return emitter;
+}
+
+function startWorker() {
+
+}

0 comments on commit 204e102

Please sign in to comment.