Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] How to migrate a Master/Worker App #3934

Closed
loretoparisi opened this issue Oct 1, 2018 · 5 comments
Closed

[QUESTION] How to migrate a Master/Worker App #3934

loretoparisi opened this issue Oct 1, 2018 · 5 comments

Comments

@loretoparisi
Copy link

My app has a master/worker Node.js clustering architecture.

Basically my run script run.js is like

'use strict';
(function() {

/// node clustering
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) { // master node
    var masterConfig=require('./config/masterconfig.json');
    
    // Fork workers.
    var maxCPUs = process.env.WORKER_NUM || masterConfig.cluster.worker.num;
    maxCPUs=(maxCPUs>numCPUs)?numCPUs:maxCPUs;

    for (let i = 0; i < maxCPUs; i++) {
        const worker=cluster.fork();
    }
    
    var MasterNode=require('./lib/master');
    var master= new MasterNode(masterConfig);
    master.start()
    .then(done=> {
        console.log(`Master ${process.pid} running on ${masterConfig.pubsub.node}`);
    })
    .catch(error=> { // cannot recover from master error
        console.error(`Master ${process.pid} error`,error.stack);
        process.exit(1);
    });
}
else if (cluster.isWorker) { // worker node
    var workerConfig=require('./config/workerconfig.json');
    var WorkerNode=require('./lib/worker');
    var worker= new WorkerNode(workerConfig);
    worker.start()
    .then(done=> {
        console.log(`Worker ${process.pid} running on ${workerConfig.pubsub.node}`);
    })
    .catch(error=> { // worker error is recoverable
        console.error(`Worker ${process.pid} error`,error.stack);
    });
}

}).call(this);

I'm starting both on the master and the workers node a HTTP server. While the workers are listening on port 3000, the master is listening on port 3001, that will be mapped to 8080 on a docker container instance:

[D O C K E R: 8080] --- N ---> [W O R K E R: 3001 ]  --- 1 ---> [M A S T E R: 3000]

Thus, a dispatcher API listening on WORKER at PORT 3001 will forward requests to the port 3000 on the MASTER when needed (e.g. for services running on the master instance only).
All MASTER / WORKER nodes shares the same codebase, but some modules are loaded once on the MASTER, while other modules duplicated on all the WORKER modules, hence I have something like:

root/
 |
 lib/
 package.json
 --- master.js
 --- worker.js
 --- run.js

How to migrate this architecture to PM2? Shall I change it? May I bypass the run.js above, or can I just use as-it-is and passing the run.js to PM2 as a service?

@wallet77
Copy link
Contributor

wallet77 commented Oct 2, 2018

Hi @loretoparisi

You can simply start your run.js with pm2. But I think it has 2 drawbacks :

  • first : pm2 will not show your app (I mean the master) but the runner (run.js) in pm2 list. And it will show it as stopped, cause the script has finished.
  • second : you will not benefit of pm2 clustering mode

So what you should do :

  1. change run.js to a pm2 ecossytem file
    See documentation here : https://pm2.io/doc/en/runtime/guide/ecosystem-file/
    You can keep your master.config.json file to get workers' number if you want.

  2. Change a little bit your code architecture
    With pm2 and its clustering mode, you don't have to manage master/workers separation.
    So you should drop your master file and keep only your workers.
    It will need some change I think in your codebase.
    Basically you will not use 2 different ports but only one.
    If you still need to launch some services just once (like you do in your master file) I will recommend to start another application for this.

@loretoparisi
Copy link
Author

loretoparisi commented Oct 2, 2018

@wallet77 thanks a lot for your suggestion. I think I should use the second approach you mention, because my application loads several frameworks that need data to be loaded once into memory so I cannot have one instance for each worker. That is why I have used the master to serve these frameworks and data loaded into memory once and the workers all the other apis.
In that case, as I understand, I should launch a separate master.js app and a worker.js app both with pm2, without doing any clustering right?

[APP 1 - W O R K E R: 3001 ]  -------> [APP 2 - M A S T E R]: 3000]

In that case how pm2 will handle the incoming / outgoing requests from worker app to master app?
Thank you.

@wallet77
Copy link
Contributor

wallet77 commented Oct 2, 2018

It really depends on what you want to achieve.

With PM2 you don't have the same flexibility as with cluster module, because pm2 act as the master and your apps is a worker.
So if you really want to keep a kind of master and share data with workers you should probably use another system to share it instead of in memory.
Because if you run master.js and worker.js with pm2 there are separated apps. So they can't communicate with each other. I mean not directly with shared memory.

@loretoparisi
Copy link
Author

loretoparisi commented Oct 3, 2018

@wallet77 thank you 👍 , I think a have the big picture now. Considering that I load resources into the master in memory and listening on port 3001 here, while the workers exposes endpoint to the rest of the world and do queries to the master from port 3000, I could run two pm2 instance anyways for each of them I assume i.e. the two pm2 services will run on different ports (3000 & 3001) and communicate via localhost apis.

@loretoparisi
Copy link
Author

closing because it's almost clear how it should work. To recap I would have two different app, one for the master and one for the worker, on different ports, both of them wrapped into PM2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants