Node provides cluster
module to create multi-process applications, so as to avoid a single process hangs cause service exceptions. The framework runs the multi-process model from the think-cluster module.
Set workers
number to specify child process number, default is 0
(the current host cpu number).
//src/config/config.js
module.exports = {
workers: 0 // can be set according to actual situation, 0 means the number of cpu
}
In multi-process model, Master will fork
workers
number of child processes, the user request is handle by Worker. If Worker process runs into exception, Master is notified to Fork a new Worker process and stop the current Worker from receiving user request.
Sometimes Workers processes need to communicate to each other, to exchange some data. But Worker process can't communicate directly, it need to use Master process to transit.
Framework provide think.messenger
to handle inter-process communication, there are several ways:
think.messenger.broadcast
will brocas message to all Worker processes
//listen to event src/bootstrap/worker.js
think.messenger.on('test', data => {
//All child process capture this this event and runs to here, including current process.
});
// src/controller/xxx.js
// trigger broadcast event
think.messenger.broadcast('test', data);
think.messenger.consume
Task consumer, only execute in one process. (Sometimes some tasks need to start on system bootstrap, and only want it to run once in one of children process)
// src/bootstrap/worker.js
think.messenger.on('consumeEvent', (data) => {
// The callback only run in one child process
});
// trigger event, it will be capture by one process
think.messenger.consume('consumeEvent', data);
think.messenger.map
execute task in all child processes, and return the task result(the result needs to call JSON.stringify before transmit, which should not be too large, for large dataset transmit can use other approachs, such as file.).
// src/bootstrap/worker.js
think.messenger.on('testMap', (data) => {
return Math.random();
});
// src/controller/xxx.js
if(xxx) {
// Get return result from all child process task, the value is Array.
// On execution only the first registered callback will be called.
const data = await think.messenger.map('testMap', data);
}
Note: consume and map method needs think-cluster version >=1.4.0
.
Sometimes built-in communication methods can not meet all the needs, time time you can customize it. For Master process execution calls src/bootstrap/master.js
, while Worker processes call src/bootstrap/worker.js
, by which the inter-process handling will be more easier.
// src/bootstrap/master.js
const cluster = require('cluster');
cluster.on('message', (worker, message) => {
// Receive specific message for handling
if(message && message.act === 'xxx'){
}
})
// src/bootstrap/worker.js
process.send({act: 'xxxx', ...args}); //send data to Master process
Sometime a general task system needs to be able to auto update itself (such as: blog system update feature), after code is changed, it needs to restart service to make it take effect, instead to manually restart the system, framework provide think-cluster-reload-workers
directive to allow child process notify master process to restart. Such as:
async upgrade() {
await downloadCodeFromRemote(); // download update package from remote
await unzipCode(); // unzip the package
await installDependencies(); // need dependencies may be added so to reinstall dependencies here
process.send('think-cluster-reload-workers'); // send master process restart directive
}