29

I've node application which done spawn(child process) to and application, the application have host and port:

var exec = require('child_process').spawn;
var child = exec('start app');
console.log("Child Proc ID " + child.pid)
child.stdout.on('data', function(data) {
    console.log('stdout: ' + data);
});
child.stderr.on('data', function(data) {
    console.log('stdout: ' + data);
});
child.on('close', function(code) {
    console.log('closing code: ' + code);
});

some application will start immediately and some application will take some time 10 - 20 sec until they start.

Now I use the node http proxy to run the app and the problem is that Im getting error when the use want to run the app before it up and running. Any idea how somehow I can solve this issue?

proxy.on('error', function (err, req, res) {
    res.end('Cannot run app');
});

Btw, I cannot send response 500 in proxy error due to limitation of our framework. Any other idea how can I track the application maybe with some timeout to see weather it send response 200.

UPDATE - Sample of my logic

httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});
http.createServer(function (req, res) {
    console.log("App proxy new port is: " + 5000)
    res.end("Request received on " + 5000);
}).listen(5000);

function proxyRequest(req, res) {
    var hostname = req.headers.host.split(":")[0];
    proxy.web(req, res, {
        target: 'http://' + hostname + ':' + 5000
    });  

    proxy.on('error', function (err, req, res) {
        res.end('Cannot run app');
    });
}
alexmac
  • 19,087
  • 7
  • 58
  • 69
  • you mention that you have framework limitations - can you go into more detail? It's hard to know what sort of solutions are available to you if we don't know what we can and can't do. Do you have control over the child process(es) that is/are being spawned? – Ieuan Stanley Jan 12 '16 at 16:27
  • What do you expect to happen if there is an request sent and app is not available yet? – Koder Feb 09 '16 at 13:15

5 Answers5

1

What you need is to listen for the first response on your proxy and look at its status code to determine whether your app started successfully or not. Here's how you do that:

proxy.on('proxyRes', function (proxyRes, req, res) {
  // Your target app is definitely up and running now because it just sent a response;
  // Use the status code now to determine whether the app started successfully or not
  var status = res.statusCode;
});

Hope this helps.

idancali
  • 847
  • 5
  • 10
  • Thanks but when and how should I do that? Assume I put your code and the user click on the browser to run the app which still is not up so first thing he will see the error, so how can I overcome this ? maybe you are an expert in this topic and I miss something here :) Thank you! –  Jan 13 '16 at 08:07
  • So if the user opens the browser and the browser makes a request to your app, then the proxy should emit the 'proxyRes' event. If your app is not ready, then the browser will not receive a response until it is. So I think this should work. – idancali Jan 13 '16 at 08:10
  • The error should not come before unless there was an actual error starting up your proxy, but if your app starts fine, then you should not get an error if you don't do anything before the 'proxyRes' event. – idancali Jan 13 '16 at 08:48
  • Thanks I've try it but it doesn't work :( any other idea? –  Jan 13 '16 at 17:23
  • It would be good to see more of your code as I can get a better sense of the entire execution flow start-to-end – idancali Jan 13 '16 at 17:52
  • HI Please see my update ,In the spawn I trigger the app and in the reverse proxy I call to it... –  Jan 13 '16 at 19:18
1

Not sure if it make sense , In your Main App the experience should start with a html page and each child process should have is own loader.

So basically , you need a http Handler, which linger the request until the the child process is ready. So just make and ajax call from the html , and show loading animation till the service is ready .

//Ajax call for each process  and update the UI accordingly   
$.get('/services/status/100').then(function(resp) {
   $('#service-100').html(resp.responseText);
})

//server side code (express syntax)
app.get('/services/status/:id ', function(req,res) {
     // Check if service is ready 
     serviceManager.isReady(req.params.id, function(err, serviceStats) {
         if(err) {
            //do logic err here , maybe notify the ui if an error occurred
            res.send(err);
            return;
         }
         // notify the ui , that the service is ready to run , and hide loader
         res.send(serviceStats);
     });
})
doron aviguy
  • 2,554
  • 2
  • 22
  • 18
1

I am not sure i understand the question correctly, but you want to wait for a child process to spin on request and you want this request to wait for this child process and then be send to it? If that is so a simple solution will be to use something like this

    var count = 0;//Counter to check
    var maxDelay = 45;//In Seconds
    var checkEverySeconds = 1;//In seconds
    async.whilst(
        function () {
            return count < maxDelay;
        },
        function (callback) {
            count++;
            self.getAppStatus(apiKey, function (err, status) {
                if (status != 200) {
                    return setTimeout(callback, checkEverySeconds * 1000);

                } 
                continueWithRequest();

            });
        },
        function (err) {
            if (err) {
                return continueWithRequest(new Error('process cannot spin!'));
            }
        }
    );

The function continueWithRequest() will forward the request to the child process and the getAppStatus will return 200 when the child process has started and some other code when it is not. The general idea is that whilst will check every second if the process is running and after 45 second if not it will return an error. Waiting time and check intervals can be easily adjusted. This is a bit crude, but will work for delaying the request and setTimeout will start a new stack and will not block. Hope this helps.

1

If you know (around) how much time the app takes to get up and running, simply add setTimeout(proxyRequest, <Time it takes the app to start in MS>)

(There are most likely more smart/complicated solutions, but this one is the easiest.)

1

Why not use event-emitter or messenger?

var eventEmitter = require('event-emitter')
var childStart = require('./someChildProcess').start()

if (childStart !== true) {
        eventEmitter.emit('progNotRun', {
            data: data
        })
} 

function proxyRequest(req, res) {

    var hostname = req.headers.host.split(":")[0];
    proxy.web(req, res, {
        target: 'http://' + hostname + ':' + 5000
    });  

    eventEmitter.on('progNotRun', function(data) {
        res.end('Cannot run app', data);
    })
}
syarul
  • 2,161
  • 2
  • 20
  • 22