Spawning multiple TCP daemons in a single app

Overview

Node applications may bind to a TCP port using listen(<PORT NUMBER>), provided of course the PORT NUMBER is one allocated to your account. Passenger replaces this listen() method with a built-in method that, instead of listening on a TCP port, creates a local UNIX socket for communication with the web server (see installServer() in source).

By creating a socket, no TCP ports are consumed, traffic may only be accessed from within the server, and the server must know the socket path. This is great for security, but if an app spawns another process, like a Socket.IO, that also calls listen(), then the app fails with:

App 28096 stderr: Error: http.Server.listen() was called more than once, which is not allowed because Phusion Passenger is in auto-install mode. This means that the first http.Server object for which listen() is called, is automatically installed as the Phusion Passenger request handler. If you want to create and listen on multiple http. Server object then you should disable auto-install mode.

Cause

listen() is overwritten to create a UNIX socket to communicate with the HTTP server, instead of a TCP socket. This obviates the need to use a proxy passthru to Node applications, but carries a limitation of only 1 listen() invocation per application.

Solution

Configure PhusionPassenger to disable overwriting listen() via autoInstall: false  and use the special port, “passenger“, to create a UNIX socket for the application that serves to handle application requests. Any subsequent daemon spawned, for example a backend job service, may operate without modification:

var http = require('http'),
 httpProxy = require('http-proxy');

// disable implicit listen() overwrite
if (typeof(PhusionPassenger) != 'undefined') {
 PhusionPassenger.configure({ autoInstall: false });
}

// explicitly listen on a Passenger socket for communication with the
// web server
httpProxy.createServer(9000, 'localhost').listen('passenger');

// create a second server on port 9000; this port should be a port
// allocated to your account
var target_server = http.createServer(function (req, res) {
 res.writeHead(200, { 'Content-Type': 'text/plain' });
 res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
 res.end();
}).listen(9000);

See also

Leave a Reply