In the principle of Node multi-process architecture, we have explained the principle of multi-process, this paper introduces its easy-to-use application module Cluster.

A single Node.js instance runs in a single thread. To make the most of a multicore system, it is sometimes necessary to enable a set of Node.js processes to handle load tasks.

Prior to Node V0.8, implementing a multi-process architecture had to be implemented through CHILD_process. Creating a standalone Node cluster was a relatively difficult task for the average engineer because there were so many details to handle.

Node V0.8 introduces the Cluster module to solve the multi-core CPU utilization problem, and also provides a comprehensive API to handle process robustness issues.

The Cluster module can create child processes that share server ports.

// Separate the parent process from the child process
// server.js
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

cluster.setupMaster({ 
  exec: "worker.js"  // This parameter is the file path of the worker process
});

var cpus = require('os').cpus(); 

for (var i = 0; i < numCPUs; i++) { 
  // After creating multiple worker processes, the program will start the corresponding n worker processes
  // The worker process is specified as the worker.js file, so it is executed n times
  cluster.fork(); 
}

// worker.js
// Worker processes can share any TCP connection.
// In this case, the HTTP server is shared.
http.createServer((req, res) = > {
  res.writeHead(200);
  res.end('Hello world \n');
}).listen(8000);

console.log('Working process${process.pid}Has launched `);
Copy the code

Executing Node server.js will have the same effect as creating a cluster of child processes. As far as official documentation is concerned, it prefers the following form as an example:

// Official example, not personally recommended
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

// Determine whether the current running process is a master process or a worker process. The master process creates multiple worker processes, and the worker process creates HTTP server applications
if (cluster.isMaster) {
  console.log(` main process${process.pid}Running ');

  // Fork the working process.
  for (let i = 0; i < numCPUs; i++) {
    // After creating multiple worker processes, the program will start the corresponding n worker processes
    // The worker process is the same file as the main process, so it executes this file n + 1 times
    cluster.fork();
  }

  cluster.on('exit'.(worker, code, signal) = > {
    console.log('Working process${worker.process.pid}Have withdrawn from `);
  });
} else {
  // Worker processes can share any TCP connection.
  // In this case, the HTTP server is shared.
  http.createServer((req, res) = > {
    res.writeHead(200);
    res.end('Hello world \n');
  }).listen(8000);

  console.log('Working process${process.pid}Has launched `);
}
Copy the code

Run the code and the worker process will share port 8000:

$ node server.jsMain process 3596 Working process 4324 Started Working process 4520 Started Working process 6056 Started Working process 5644 startedCopy the code

Whether a process is a master process or a worker process depends on whether NODE_UNIQUE_ID is present in the environment variable, as shown below:

cluster.isWorker = ('NODE_UNIQUE_ID' in process.env); 
cluster.isMaster = (cluster.isWorker === false);
Copy the code

However, the official example, which determines cluster. IsMaster and cluster. IsWorker, makes the code very readable. I recommend using the cluster.setupMaster() API to completely strip the main and worker processes from the code, as the send() method looks like sending the server directly from the main process to the child process. After stripping the code, you won’t even feel that there is any server-related code in the main process.

Using cluster.setupMaster() to create child processes instead of using cluster.fork(), the program structure is no longer messy, logical, and the code is readable and maintainable.

Working Principle of Cluster

In fact, the Cluster module is a combination of the CHILD_process and NET modules.

When a cluster starts, it starts the TCP server internally, as we did in the Node multi-process architecture principle, and sends the file descriptor for the TCP server socket to the worker process when the cluster.fork() child process is executed.

If the process is copied from cluster.fork(), NODE_UNIQUE_ID will be in its environment variable. If the worker process has listen() on the network port, it will get the file descriptor and reuse it through the SO_REUSEADDR port. In this way, many sub-processes share ports. For processes started in the normal way, there is no such thing as file descriptors passing shares.

The way TCP servers are created implicitly within the cluster is transparent to consumers, but it does not allow for the same flexibility as using child_process directly. In a Cluster module application, a main process can manage only one group of worker processes, as shown below:

When you operate on your own through child_process, you have more flexibility to control worker processes, or even groups of worker processes. The reason for this is that when operating on child processes by child_process, you can implicitly create multiple TCP servers so that child processes can share multiple server-side sockets, as shown below:

Cluster events

For robust processing, the Cluster module also exposes quite a few events.

  • Fork: Triggers this event after a worker process is replicated.
  • Online: After replicating a worker process, the worker process sends an online message to the master process. The master process triggers this event after receiving the message.
  • Listening: After a worker process calls LISTEN () (the server Socket is shared), a listening message is sent to the master process. The master process receives the message and triggers the event.
  • Disconnect: This event is triggered when the IPC channel between the main process and the worker process is disconnected.
  • Exit: This event is triggered when a worker process exits.
  • Setup: cluster.setupMaster() triggers this event.

Most of these events are related to events in the CHILD_process module, which is encapsulated on the basis of interprocess messaging. These events are sufficient to enhance the robustness of your application.

For more details: nodejs.cn/api/cluster…

reference

  1. NodeJs by Park Ling