Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Node .js cluster


May 10, 2021 Node.js


Table of contents


Cluster

稳定性: 2 - 不稳定

A single Node.js instance runs on a single thread, and in some cases it can be loaded, so to make better use of the capabilities of multi-core systems, you can use Node.js.js's built-in cluster capabilities to handle the load.

It is easy to create a process that shares all server interfaces in the cluster module.

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection
  // In this case its a HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

When Node is run, 8000 ports will be shared across all work processes.

% NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online

This feature has only recently been introduced and you can try it out and provide feedback.

Also note that you cannot create a named pipe server in a working process in a Windows system.

How it works

child_process.fork so it can communicate through the IPC and parent processes and pass handles to each other.

The cluster module handles connections through two distribution modes:

The first (the default method, except for windows platforms) is circular. The main process listens to a port, receives new connections, and then takes turns distributing to the working process.

Second, the main process listens to the socket and sends it to the work process of interest, which receives the connection directly.

Comparing the above two methods, the second method theoretically performs best. I n fact, because of the variety of operating systems, distributions are often uneassisted. For example, 70% of connections terminate in two processes, and there are actually eight processes.

Because server.listen() leaves most of the work to the main process, a common Node.js process and a cluster work process can be different in three cases:

  1. server.listen({fd: 7}) message is passed back to the main process, the file descriptor in the main process is listened to instead of the file descriptor 7 in the other working process.
  2. server.listen(handle) listens to an explicit handle that causes the work process to use the specified handle instead of communicating with the main process. If the work process already has the handle, as long as you know what you're doing.
  3. server.listen(0) lets the server listen to the port at random. H owever, each work process in the cluster receives the same port when listen(0) I n fact, only the first time is random, and then predictable. I f you want to listen to a particular port, you can produce a port ID based on the ID of the cluster's working process.

There is no .js route logic in The Node or in your program, and there is no shared state for the working process. Therefore, work such as logins and sessions should not be designed to rely too heavily on objects in memory.

Because worker threads are independent, you can kill or derive them on demand without affecting other processes. T he server also receives connections as long as there are still working processes. Node does not automatically manage the number of processes, which is your responsibility and you can manage them according to your needs.

cluster.schedulingPolicy

Scheduling policy cluster.SCHED_RR rotation, cluster.SCHED_NONE operating system processing. This is a global setting, and once you derive the first work process through cluster.setupMaster() it cannot be changed.

SCHED_RR the default setting for all systems except Windows. As long as libv is able to allocate IOCP handles efficiently and does not result in significant performance losses, Windows will SCHED_RR mode.

cluster.schedulingPolicy also be changed NODE_CLUSTER_SCHED_POLICY NODE_CLUSTER_SCHED_POLICY variables. The valid "rr" "none"

cluster.settings

  • {Object}
    • execArgv (Array) is passed to the executable Node's list of process.execArgv )
    • The path to the exec execution file. (Default s process.argv[1] )
    • args passed to the work process by args process.argv.slice(2) )
    • silent the silent (Boolean) sends the output to the stdio of the parent process. (The default is false )
    • uid Number sets the ID of the user process. ( Refer to setuid (2). )
    • gid Number sets the ID of the process group. ( Refer to setgid (2). )

When .setupMaster() .fork() method is called, the settlings object contains the settings, including the default values.

The setting freezes immediately because .setupMaster() be called once.

This object should not be changed or set manually.

cluster.isMaster

  • {Boolean}

If it is the main process, return true. If process.env.NODE_UNIQUE_ID undefined, isMaster is true

cluster.isWorker

  • {Boolean}

If the main process does not return true (as cluster.isMaster

Event: 'fork'

  • worker {Worker object}

When a new work process is branched out, the cluster module generates a 'fork' event. It can be used to record work processes and create your own time-out management.

var timeouts = [];
function errorMsg() {
  console.error("Something must be wrong with the connection ...");
}

cluster.on('fork', function(worker) {
  timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', function(worker, address) {
  clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', function(worker, code, signal) {
  clearTimeout(timeouts[worker.id]);
  errorMsg();
});

Event: 'online'

  • worker {Worker object}

After a new work process is spent, it responds to an online message. W hen the main thread receives an online message, it triggers the event. T he difference between 'fork' and 'online' is that the main process branch calls fork after a working process, and the work process calls emitted when it runs.

cluster.on('online', function(worker) {
  console.log("Yay, the worker responded after it was forked");
});

Event: 'listening'

  • worker {Worker object}
  • address {Object}

When a working listen() the server triggers a 'listening' event, which is also triggered in the cluster of the main process.

The event handler has two parameters, worker the worker's working address contains the following address port addressType These things are useful if the work process is listening to multiple addresses.

cluster.on('listening', function(worker, address) {
  console.log("A worker is now connected to " + address.address + ":" + address.port);
});

addressType is the following:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (unix domain socket)
  • "udp4" "udp6" (UDP v4 or v6)

Event: 'Disconnect'

  • worker {Worker object}

This event is triggered when the IPC channel of a working process is closed. It is called when the worker exits normally, is killed, or is manually shut down (e.g. worker.disconnect().)

disconnect be a exit between the disconnect and exit events. These events can be used to detect if the process is stuck in the cleanup process, or if there is a long connection.

cluster.on('disconnect', function(worker) {
  console.log('The worker #' + worker.id + ' has disconnected');
});

Event: 'exit'

  • worker {Worker object}
  • code . . . if you exit normally, it's an exit code.
  • signal name (for example, 'SIGHUP' that causes the process to be killed )

When any one of the work processes terminates, the cluster module triggers an 'exit' event.

You can call .fork() restart the work process.

cluster.on('exit', function(worker, code, signal) {
  console.log('worker %d died (%s). restarting...',
    worker.process.pid, signal || code);
  cluster.fork();
});

See child_process event: 'exit' .

Event: 'setup'

  • settings {Object}

The .setupMaster() when it is called.

settings object is cluster.settings object.

See cluster.settings

cluster.setupMaster([settings])

  • settings {Object}
    • The path to the exec execution file. (Default s process.argv[1] )
    • args passed to the work process by args process.argv.slice(2) )
    • silent the silent (Boolean) sends the output to the stdio of the parent process.

setupMaster used to change the default 'fork'. Once called, the settings value will appear cluster.settings

Here are some things to keep in mind:

  • Changing any settings will only affect future work processes and will not affect processes that are already running
  • In a work process, only the env property .fork() can be changed.
  • The above default values are valid only on the first call, followed by cluster.setupMaster() is called.

For example:

var cluster = require('cluster');
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'https'],
  silent: true
});
cluster.fork(); // https worker
cluster.setupMaster({
  args: ['--use', 'http']
});
cluster.fork(); // http worker

Can only be called in the main process.

cluster.fork([env])

  • env added to the sub-process environment variable by env .Object.
  • return {Worker object}

Derive a new work process.

Can only be called in the main process.

cluster.disconnect([callback])

  • callback is called when all work processes are disconnected and the handle is closed.

cluster.workers can call .disconnect() off.

Allows the main process to exit gracefully when all internal handle connections are closed and there are no events waiting to be processed.

This method has an optional parameter that is called upon completion.

Can only be called in the main process.

cluster.worker

  • {Object}

A reference to the object of the current working process. Not available in the main process.

var cluster = require('cluster');

if (cluster.isMaster) {
  console.log('I am master');
  cluster.fork();
  cluster.fork();
} else if (cluster.isWorker) {
  console.log('I am worker #' + cluster.worker.id);
}

cluster.workers

  • {Object}

A hash table that stores active working objects, the primary id which makes it easy to traverse all work processes and is available only in the main process.

When the work process closes the connection and exits, it is removed from the club.workers. The order of these two events cannot be determined and only guarantees that removal from cluster.workers 'disconnect' 'exit'

// Go through all workers
function eachWorker(callback) {
  for (var id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker(function(worker) {
  worker.send('big announcement to all workers');
});

If you want to refer to a work process through a communication channel, it is easiest to query using the id of the working process.

socket.on('data', function(id) {
  var worker = cluster.workers[id];
});

Class: Worker

A Worker object contains all the information and methods that are exposed by the worker process. It can be obtained in the cluster.workers and in the worker cluster.worker

worker.id

  • {String}

Each new work process has its own unique label, and it is id

When the work process is available, id is the primary key in cluster.workers.

worker.process

  • {ChildProcess object}

All working processes are created by child_process.fork(), which returns objects that are stored in the process.

See also: Child Process Module

Note: process and .suicide true 'disconnect' triggered and the working process is process.exit(0) It protects unexpected connection shutdowns.

worker.suicide

  • {Boolean}

Set .kill() .disconnect() before which it is undefined

worker.suicide you tell whether to exit voluntarily or unexpectedly, and the main process can use this value to decide whether to re-send the build worker process.

cluster.on('exit', function(worker, code, signal) {
  if (worker.suicide === true) {
    console.log('Oh, it was just suicide\' – no need to worry').
  }
});

// kill worker
worker.kill();

worker.send(message[, sendHandle])

  • message {Object}
  • sendHandle {Handle object}

This function is the child_process the send method provided by the new .fork(). I n the main process you must use this function to message the specified working process.

In a work process, you process.send(message)

This example responds to all messages from the main process:

if (cluster.isMaster) {
  var worker = cluster.fork();
  worker.send('hi there');

} else if (cluster.isWorker) {
  process.on('message', function(msg) {
    process.send(msg);
  });
}

worker.kill([signal='SIGTERM'])

  • signal of the kill signal sent to the work process by signal .String

This function kills the working process. I n the main process, it shuts worker.process which sends a kill signal once it is turned off. In the work process, close the channel, exit, and return code 0

causes .suicide be set.

To maintain compatibility, this method is alias worker.destroy()

Note that there is process.kill() which is different.

worker.disconnect()

In a working process, this function shuts down all servers, waits for the 'close' event, and shuts down the IPC channel.

In the main process, an internal message is sent to the work process to .disconnect()

causes .suicide be set.

Note that when the server is down, new connections are no longer accepted, but new listening can be accepted. C onnections that already exist allow normal exit. When the connection is empty, the IPC channel of the working process runs an elegant exit.

The above applies only to server connections, which are closed by the working process.

Note that in a working process, process.disconnect but it's not this function, it's disconnect.

Since long connections can block processes from shutting down connections, a better approach is to send a message to the app so that the app will find a way to close them. Time-out management is also good, and if the disconnect event has not been triggered after a disconnect time, the process will be killed.

if (cluster.isMaster) {
  var worker = cluster.fork();
  var timeout;

  worker.on('listening', function(address) {
    worker.send('shutdown');
    worker.disconnect();
    timeout = setTimeout(function() {
      worker.kill();
    }, 2000);
  });

  worker.on('disconnect', function() {
    clearTimeout(timeout);
  });

} else if (cluster.isWorker) {
  var net = require('net');
  var server = net.createServer(function(socket) {
    // connections never end
  });

  server.listen(8000);

  process.on('message', function(msg) {
    if(msg === 'shutdown') {
      // initiate graceful close of any connections to server
    }
  });
}

worker.isDead()

The work process ends and true otherwise false

worker.isConnected()

When the work process connects to the main process through the IPC channel, true true otherwise false W hen a work process is created, it connects to the main process. The disconnect closed when the disconnect event is triggered.

Event: 'message'

  • message {Object}

This event is child_process.fork() You should use this event in the main process, and you can process.on('message')

For example, a cluster uses a messaging system to count the number of requests in the main process:

var cluster = require('cluster');
var http = require('http');

if (cluster.isMaster) {

  // Keep track of http requests
  var numReqs = 0;
  setInterval(function() {
    console.log("numReqs =", numReqs);
  }, 1000);

  // Count requestes
  function messageHandler(msg) {
    if (msg.cmd && msg.cmd == 'notifyRequest') {
      numReqs += 1;
    }
  }

  // Start workers and listen for messages containing notifyRequest
  var numCPUs = require('os').cpus().length;
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  Object.keys(cluster.workers).forEach(function(id) {
    cluster.workers[id].on('message', messageHandler);
  });

} else {

  // Worker processes have a http server.
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");

    // notify master about the request
    process.send({ cmd: 'notifyRequest' });
  }).listen(8000);
}

Event: 'online'

Similar cluster.on('online') events, it can only be triggered in a particular work process.

cluster.fork().on('online', function() {
  // Worker is online
});

It is not triggered in the work process.

Event: 'listening'

  • address {Object}

Similar cluster.on('listening') can only be triggered in a particular work process.

cluster.fork().on('listening', function(address) {
  // Worker is listening
});

It is not triggered in the work process.

Event: 'Disconnect'

Similar cluster.on('disconnect') can only be triggered in a particular work process.

cluster.fork().on('disconnect', function() {
  // Worker has disconnected
});

Event: 'exit'

  • code code at the normal exit of code .
  • signal of the signal (such as SIGHUP) of the signal that causes the process SIGHUP

Similar cluster.on('exit') events, it can only be triggered in a particular work process.

var worker = cluster.fork();
worker.on('exit', function(code, signal) {
  if( signal ) {
    console.log("worker was killed by signal: "+signal);
  } else if( code !== 0 ) {
    console.log("worker exited with error code: "+code);
  } else {
    console.log("worker success!");
  }
});

Event: 'error'

Similar child_process.fork() event.

You can also use process.on('error')