A single instance of Node.js runs in a single tread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load.
The cluster module allows you to easily create child processes that all share server port.
if (cluster.isMaster) { console.log(`Master ${process.pid} is running`)
// Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() }
cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`) }) } else { // Workers can share any TCP connection // In this case it is an HTTP Server
Running Node.js will now share port 8000 between the workers.
How it works
The workder processes are spawned using the child_process.fort() method, so that they can communicate with the parent via IPC and pass server handles back and forth.
The cluster module supports two methods of distributing incoming connections.
The first one (and the default one on all platform except Windows), is the round-robin approch, where the master process listens on a port, accepts new connections and distributes them across the workers in a round-robin fashion, with some built-in smarts to avoid overloading a worker process.
The second approach is where the master process creates the listen socket and sends it to interested workers. The workers then accept incoming connection directly.
The second approach should, in theory, give the best performance. In practice however, distribution tends to be very unbalanced due to operating system scheduler vagaries. Loads have been observed where over 70% of all connections ended up in just two process, out of a total of eight.
Because server.listen() hands off most of the worker to the master process, there are three case where the behavior between a normal Node.js process and a cluster worker differs:
server.listen({fd: 7}) Because the message is passed to the master, file descriptor 7 in the parent will be listened on, and the handle passed to the worker, rather than listening to the worker’s idea of what the number 7 file descriptor reference. (the fd will be used up sooner)
server.listen(handle) Listening on handles explicitly wil cause the worker to use the supplied handle, rather than talk to the master process. If the worker already has the handle, then it’s presumed that you know what you are doing.
server.listen(0) Normally, this will cause servers to listen on a random port. However, in a cluster, each worker will receive the same ‘random’ port each time they do listen(0). In essence, the port is random the first time, but predicatable thereafter. If you want to listen on a unique port, generate a port number based on the cluster worker ID.
There is no routing logic in Node.js, or in your program, and no shared state between the workers. Therefore, it is important to design your program such that it does not rely on heavily on in-memory data object for things like sessions and login.
Because workers are all separate processes, they can be killed or re-spawned depending on your program’s needs, without affecting other workers. As long as there are some workers still alive, the server will continue to accept connections. If no workers are alive, existing connections will be dropped and new connections will be refused. Node.js does not automatically manage the number of worker for you, however. It is yoru responsibility to manage the worker pool for your application’s needs.
Class: worker
A Worder Object contains all public information and method about a worker. In the master it can be obtained using cluster.workers. In a worker it can be obtained using cluster.worker
Event: ‘disconnect’
Similar to the cluster.on('disconnect') event, but specific to this worker.
javascript
1 2 3
cluster.fork().on('disconnect', () => { // Worker has disconnected })
Event: ‘error’
This event is the same as the one provided by child_process.fork()
In a worker you can alse use process.on('error')
Event: ‘exit’
code
the exit code, if it exited Normally
signal
the name of the signal(e.g. SIGHUP) that caused the process to be killed.
Similar to the cluster.on('exit') event, but specific to this worker
javascript
1 2 3 4 5 6 7 8 9 10
const worker = cluster.fork() worker.on('exit', (code, signal) => { if (signal) { console.log(`worker was killed by signal: ${signal}`) } elseif (code !== 0) { console.log(`worker exited with error code: ${code}`) } else { console.log('worker success') } })