0

I need to run multiple Node apps on the same port. I've found out that I can run multiple node apps using one port, thanks to this SO question Running multiple Node (Express) apps on same port But it's not working for me probably bec. I'm using Restify unless I did something wrong somewhere.

I already have "app1" running on this one port using PM2 built using Restify. I've made another app "app2". The paths are like these:

/var/www/app1
/var/www/app2

with each app having common routes like these:

app.get('/', func...);
app.get('/about', func...);
app.post('/foo', func...);
app.post('/bar', func...);

I've set up "app1"'s last lines of code as: exports.app = app instead of app.listen(8080, function() { ... });

and, where app is

var app = restify.createServer({
    name: 'app1'
});

"app2" is the same as well...

My main.js file (which is saved in /var/www/) is also built on Restify:

main
 .use('/app`', require('./app1/index').app)
.listen(8080);

where main is

var main = restify.createServer({
    name: 'main'
});

But I'm getting an error such as this when I type node main.js (I haven't tried with PM2 yet):

/var/www/node_modules/restify/node_modules/assert-plus/assert.js:45
                    throw new assert.AssertionError({
                          ^
AssertionError: handler (function) is required
    at process (/var/www/node_modules/restify/lib/server.js:76:24)
    at argumentsToChain (/var/www/node_modules/restify/lib/server.js:84:13)
    at Server.use (/var/www/node_modules/restify/lib/server.js:625:6)
    at Object.<anonymous> (/var/www/main.js:47:8)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)

Note: I've turned off all the apps running under PM2. There are no node apps running on any port.

Community
  • 1
  • 1
junerockwell
  • 838
  • 1
  • 9
  • 29
  • you can't.... but you can run multiple instances of same app.. in multi core system! – vkstack Mar 21 '16 at 19:04
  • app.use() is specific to express and cannot be used to forward requests to anything other than another express "app". See my answer below for the only way to support heterogeneous servers. – Rob Raisch Mar 21 '16 at 19:31

1 Answers1

1

The only way to do this effectively is to run an HTTP proxy configured to answer requests on a single port and pass them, based upon URL patterns, to servers running on other ports, a simple example of which can be found at A HTTP Proxy Server in 20 Lines of node.js Code.

In essence, your publicly visible proxy server runs on port 80 and you run other servers to handle specific requests.

So for example, if you run three HTTP servers, one as a forwarding proxy and two for specific functions such that:

  • proxy on port 80
  • server2 on port 8080 for requests matching regexp:/^\/first(?:\/.*)?$/
  • server3 on port 8081 for requests matching regexp:/^\/second(?:\/.*)?$/

where the only server that has a public connection is your proxy.

When the proxy receives a request for /first or /first/index.html, it forwards the request to server2 which returns a result document that the proxy then sends back to the original requester.

When it receives a request for /second/foo/bar/page.html, it does the same but using server3 to produce a result.

http-proxy is an implementation of this strategy which uses the http-proxy-rules plugin to process and forward requests based on URL patterns.

UPDATE

For the purposes of clarity, we assume proxy, server2, and server3 above represent individual node HTTP server instances listening on a single IP address but separate ports on the same machine.

Example:

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

// Set up proxy rules instance
//   where 
//     any request for /hostname/app1 will be proxy-ed via SERVER2
//     any request for /hostname/app2 will be proxy-ed via SERVER3
var proxyRules = new HttpProxyRules({
  rules: {
    '.*/app1/': 'http://localhost:8080',  // TO SERVER2
    '.*/app2/': 'http://localhost:8081'   // TO SERVER3
  }
});

// Create reverse proxy instance
var proxy = httpProxy.createProxy();

// Create http server on hostname:80 that leverages reverse 
// proxy instance and proxy rules to proxy requests to 
// different one of two target servers
http.createServer(function(req, res) { // PROXY 

  // a match method is exposed on the proxy rules instance
  // to test a request to see if it matches against one 
  // of the specified rules
  var target = proxyRules.match(req);

  if (target) {
    return proxy.web(req, res, {
      target: target
    });
  }

  res.writeHead(500, { 'Content-Type': 'text/plain' });
  res.end('No rule found for this request');

}).listen(80);

// create a new HTTP server on localhost:8080 to process
// requests sent from the proxy
http.createServer(function (req, res) { // SERVER2
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  var headers=JSON.stringify(req.headers,true,2);
  res.write('request successfully proxy-ed to SERVER2!' + '\n' + headers);
  res.end();
}).listen(8080,'localhost');

// create a new HTTP server on localhost:8081 to process
// requests sent from the proxy
http.createServer(function (req, res) { // SERVER3
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  var headers=JSON.stringify(req.headers,true,2);
  res.write('request successfully proxy-ed to SERVER3!' + '\n' + headers);
  res.end();
}).listen(8081,'localhost');

Using this setup:

  • only the proxy server will be available externally on port 80
  • the servers running on ports 8080 & 8081 are only available on the local machine
  • requests received on the proxy at hostname:80 that match the /app1 path (and descendants) will be proxy-ed by the server running on localhost:8080
  • requests received on the proxy at hostname:80 that match the /app2 path (and descendants) will be served by the server running on localhost:8081
Rob Raisch
  • 17,040
  • 4
  • 48
  • 58
  • Thanks for this suggestion. It works by giving server2 and server3 their own ports. But the https://www.npmjs.com/package/http-proxy-rules manual shows that server2 and server3 can have the same port. I tried it this way but it didn't work. I do need server2 and server3 to have the same port where I just do `rules: { '.*/test': 'http://localhost:8080/server2', // Rule (1) '.*/test2/': 'http://localhost:8080/server3/' // Rule (2) },` and then listen on port 8000. Do you know how to? – junerockwell Mar 21 '16 at 21:55
  • Sorry, but you cannot run more than one listener per port, no matter how hard you try. That's a limit of the operating system's TCP/IP stack and there is no way around it. The example you cite does not run two servers on the same port. Rather, it redirects to two URLs served by the same server on port 8080. – Rob Raisch Mar 21 '16 at 22:34
  • Hi @Rob Raisch, Thanks for explaining, but that's what I thought; *the same server on the same port.* How do I use the same hostname and port number for 2 node apps (you named server2 and server3 in your answer)? I don't understand how `server2` is created as part of the url and how it works in `http://localhost:8080/server2`? I understand that if I type `http://localhost:8080/test` it redirects to `http://localhost:8080/server2`. But I don't know how to make `http://localhost:8080/server2` url so that I can redirect to the appropriate node app? is `.../server2` a directory? – junerockwell Mar 22 '16 at 15:06
  • Perhaps I've not been clear. You cannot run more than one listener per IP address and TCP port number pair and since the hostname `localhost` is shorthand for the IP address (127.0.0.1) of the "loopback" network interface on your server host, you **cannot** run more than one process on `127.0.0.1:8080`. Full stop. – Rob Raisch Mar 22 '16 at 20:26
  • Regarding the http-proxy-rules example code, you'll note that rather than running multiple servers on the same port, it describes proxying requests to different target paths _on the same server_ running on `localhost:8080`. – Rob Raisch Mar 23 '16 at 13:17
  • would you mind showing a demo about that? I specifically want to understand how the paths are created, and once proxy redirects to a url, say `localhost:8080/server2`, does the node app `server2` have its own port number aside from `8080`? In other words, does `localhost:8080/server2` pointing to `localhost:8888` or something? How does it point to the right node app? *Sorry but I'm new to the whole proxy thing.* – junerockwell Mar 23 '16 at 15:08
  • The example provided in the README for [http-proxy-rules](https://www.npmjs.com/package/http-proxy-rules) runs a single proxy server on `localhost:6010` which proxies requests to another plain http server that the example assumes exists and is running but is not specifically defined in the example. The decision what to proxy and where is defined in the `proxyRules` object. To be clear, the example assumes two servers: the proxy which is defined and another plain http server which is not defined in the example and must be running on `localhost:8080` for the example to work. – Rob Raisch Mar 23 '16 at 15:29