May 10, 2021 Node.js
稳定性: 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.
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:
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.
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.
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.
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"
execArgv
(Array) is passed to the executable Node's list of
process.execArgv
)
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.
If it is the main process, return true.
If
process.env.NODE_UNIQUE_ID
undefined,
isMaster
is
true
If the main process does not return true (as
cluster.isMaster
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();
});
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");
});
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)
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');
});
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' .
settings
{Object}
The
.setupMaster()
when it is called.
settings
object is
cluster.settings
object.
See
cluster.settings
settings
{Object}
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:
env
property
.fork()
can be changed.
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.
env
added to the sub-process environment variable by env .Object.
Derive a new work process.
Can only be called in the main process.
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.
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);
}
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];
});
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
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.
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.
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();
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);
});
}
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.
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
}
});
}
The work process ends and
true
otherwise
false
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.
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);
}
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.
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.
Similar
cluster.on('disconnect')
can only be triggered in a particular work process.
cluster.fork().on('disconnect', function() {
// Worker has disconnected
});
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!");
}
});
Similar
child_process.fork()
event.
You can also use
process.on('error')