Unveiling Node.js: How Processes, Threads, and Clusters Really Work
This article explains Node.js's internal concurrency model, covering the distinction between processes and threads, debunking the single‑thread myth, and detailing how the event loop, child processes, cluster mode, and worker_threads enable high‑performance, multi‑core execution.
Concepts
A process is an independent execution unit that the operating system schedules and allocates resources to, while a thread is a lightweight execution flow that lives inside a process and shares its resources.
Is Node.js Single‑Threaded?
Many developers claim that Node.js is single‑threaded, but a Node process actually contains multiple native threads.
# 示例一
require('http').createServer((req, res) => {
res.writeHead(200);
res.end('Hello World');
}).listen(8000);
console.log('process id', process.pid);Running top -pid <pid> shows the #TH column with 7 threads, indicating that the process includes:
1 JavaScript main thread
1 watchdog thread for debugging information
1 V8 task‑scheduler thread
4 V8 worker threads for JIT compilation and GC
Libuv thread‑pool threads for asynchronous I/O
The size of the libuv I/O thread pool defaults to 4 when the program performs I/O; it can be changed with the process.env.UV_THREADPOOL_SIZE environment variable.
// V8 initialization thread pool
const int thread_pool_size = 4; // default 4 threads
default_platform = v8::platform::CreateDefaultPlatform(thread_pool_size);
V8::InitializePlatform(default_platform);
V8::Initialize();Event Loop
Node uses libuv's event loop ( uv_event_loop) to achieve non‑blocking I/O. When the JavaScript thread encounters a blocking operation, the work is handed off to the libuv thread pool, and the result is later delivered to the main thread via a callback.
Child Processes
For CPU‑intensive tasks that would block the main thread, Node provides the child_process module to spawn separate processes.
spawn : creates a child process with streams for stdin/stdout/stderr.
exec : runs a command in a shell and buffers the output.
execFile : similar to exec but does not spawn a shell, offering slightly better performance.
fork : a specialized spawn for Node scripts that sets up an IPC channel.
Parent and child processes communicate via the IPC channel using process.send and the message event.
Example
# 主进程 (main_process.js)
const { fork } = require('child_process');
const child = fork('./fib.js');
child.send({ num: 44 });
child.on('message', message => {
console.log('receive from child process, calculate result: ', message.data);
child.kill();
});
child.on('exit', () => {
console.log('child process exit');
});
setInterval(() => {
console.log('continue execute javascript code', new Date().getSeconds());
}, 1000); # 子进程 (fib.js)
function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);
}
process.on('message', msg => {
const { num } = msg;
const data = fib(num);
process.send({ data });
});
process.on('SIGHUP', () => process.exit()); child pid 39974
continue execute javascript code 41
continue execute javascript code 42
continue execute javascript code 43
continue execute javascript code 44
receive from child process, calculate result: 1134903170
child process exitCluster Mode
The cluster module (available since Node v0.6) implements a master‑worker model for load‑balanced multi‑process servers. On Unix‑like systems it uses round‑robin scheduling; on Windows it uses a shared handle to pass sockets to workers.
# Cluster example (calculating Fibonacci numbers)
const cluster = require('cluster');
function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);
}
if (cluster.isMaster) {
for (let i = 43; i < 45; i++) {
const worker = cluster.fork();
worker.send({ num: i });
worker.on('message', message => {
console.log(`receive fib(${message.num}) calculate result ${message.data}`);
worker.kill();
});
}
cluster.on('exit', worker => {
console.log('worker ' + worker.process.pid + ' killed!');
if (Object.keys(cluster.workers).length === 0) {
console.log('calculate main process end');
}
});
} else {
process.on('message', message => {
const { num } = message;
const data = fib(num);
process.send({ data, num });
});
}Worker Threads
Since Node v10, the worker_threads module allows creating multiple threads inside a single process, suitable for CPU‑bound work without blocking the event loop.
Creation
const { Worker } = require('worker_threads');
const worker = new Worker('worker.js');Communication via parentPort
const { Worker, isMainThread, parentPort } = require('worker_threads');
function fib(num) { /* same implementation as above */ }
if (isMainThread) {
const worker = new Worker(__filename);
worker.once('message', message => {
console.log(`Fibonacci(${message.num}) is ${message.result}`);
process.exit();
});
worker.postMessage(43);
} else {
parentPort.once('message', num => {
const result = fib(num);
parentPort.postMessage({ num, result });
});
}MessageChannel for Direct Thread‑to‑Thread Communication
const { isMainThread, parentPort, MessageChannel, Worker } = require('worker_threads');
if (isMainThread) {
const worker1 = new Worker(__filename);
const worker2 = new Worker(__filename);
const subChannel = new MessageChannel();
worker1.postMessage({ port: subChannel.port1 }, [subChannel.port1]);
worker2.postMessage({ port: subChannel.port2 }, [subChannel.port2]);
} else {
parentPort.once('message', value => {
value.port.postMessage(`Hi, I am thread${value.port}`);
value.port.on('message', msg => {
console.log(`thread${value.port} receive: ${msg}`);
});
});
}Summary
Node.js runs JavaScript on a single main thread but relies on libuv's thread pool, child processes, the cluster module, and worker_threads to achieve high concurrency, handle I/O efficiently, and leverage multi‑core CPUs for CPU‑intensive workloads.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
