Node.js 并发模型与线程池
理解 Node.js 的并发模型,是优化高并发应用的关键。
单线程并发模型
JavaScript
主线程(单线程)
│
├── 事件循环
│ ├── 处理 JavaScript 回调
│ └── 调度 I/O 操作
│
└── libuv 线程池(4 线程)
├── 文件系统操作
├── 加密操作
├── 压缩操作
└── DNS 解析
为什么单线程能高并发
JavaScript
// 单线程不阻塞,等待时处理其他请求
server.on('request', (req, res) => {
// 发起异步 I/O
fs.readFile('file.txt', (err, data) => {
res.end(data);
});
// 主线程继续处理下一个请求
// 不等待文件读取完成
});
libuv 线程池
JavaScript
// 线程池配置
// 默认 4 个线程
process.env.UV_THREADPOOL_SIZE = 8;
// 线程池处理的操作
// fs.readFile/writeFile
// crypto.pbkdf2/hash
// zlib.compress/decompress
// dns.lookup
// uv_queue_work(自定义)
线程池大小影响
JavaScript
// UV_THREADPOOL_SIZE = 4(默认)
// 同时最多 4 个文件操作并发
// 测试线程池瓶颈
const fs = require('fs');
const start = Date.now();
for (let i = 0; i < 8; i++) {
fs.readFile('large.txt', () => {
console.log(`文件 ${i}: ${Date.now() - start}ms`);
});
}
// UV_THREADPOOL_SIZE=4
// 文件 0-3: ~200ms(并发)
// 文件 4-7: ~400ms(排队等待)
// UV_THREADPOOL_SIZE=8
// 文件 0-7: ~200ms(全部并发)
非线程池 I/O
JavaScript
// 网络 I/O 不使用线程池
// 使用系统原生异步机制
// Linux: epoll
// macOS: kqueue
// Windows: IOCP
// 网络连接数不受线程池限制
const net = require('net');
const server = net.createServer();
server.listen(3000);
// 可处理数万并发连接(不受线程池影响)
worker_threads 多线程
JavaScript
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
// 主线程
const worker = new Worker(__filename);
worker.on('message', (result) => {
console.log('结果:', result);
});
worker.postMessage({ data: '发送数据' });
} else {
// Worker 线程
parentPort.on('message', (msg) => {
const result = heavyComputation(msg.data);
parentPort.postMessage(result);
});
}
// Worker 线程特点
// - 真正的多线程
// - 独立 V8 实例
// - 可共享内存
Worker 共享内存
JavaScript
const { Worker, SharedArrayBuffer } = require('worker_threads');
// 创建共享内存
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
const worker = new Worker('./worker.js', {
workerData: { sharedArray }
});
// Worker 可直接访问和修改
// 无需消息传递开销
cluster 集群模式
JavaScript
const cluster = require('cluster');
const http = require('http');
if (cluster.isMaster) {
// 主进程
const cpuCount = require('os').cpus().length;
// 启动多个工作进程
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log('工作进程退出:', worker.process.pid);
cluster.fork(); // 重启
});
} else {
// 工作进程
http.createServer((req, res) => {
res.end('Hello');
}).listen(3000);
}
// 多进程特点
// - 多个 Node.js 进程
// - 利用多核 CPU
// - 主进程分发请求
并发模型对比
| 模型 | 特点 | 适用场景 |
|---|---|---|
| 单线程事件循环 | 高并发 I/O | Web 服务器 |
| libuv 线程池 | 阻塞操作并行 | 文件/加密 |
| worker_threads | CPU 密集计算 | 数据处理 |
| cluster | 多核利用 | 高负载服务 |
CPU 密集任务处理
JavaScript
// ❌ 主线程执行阻塞事件循环
function heavyComputation() {
for (let i = 0; i < 1e9; i++) {
// 阻塞 1 秒
}
}
// ✅ 使用 Worker 线程
const worker = new Worker('./compute.js');
worker.postMessage({ task: 'compute' });
worker.on('message', result => {
console.log('计算完成:', result);
});
// ✅ 分片执行
function chunkedCompute() {
let iterations = 1e9;
const chunk = 1e6;
function processChunk() {
for (let i = 0; i < chunk; i++) {
iterations--;
}
if (iterations > 0) {
setImmediate(processChunk);
}
}
processChunk();
}
并发性能调优
text
// 1. 调整线程池大小
process.env.UV_THREADPOOL_SIZE = Math.max(4, cpuCount);
// 2. 使用 Worker 处理 CPU 密集任务
// 3. 使用 cluster 利用多核
// 4. 监控性能
const { performance } = require('perf_hooks');
const obs = new performance.Observer((list) => {
const entries = list.getEntries();
entries.forEach(entry => {
console.log(`${entry.name}: ${entry.duration}ms`);
});
});
obs.observe({ entryTypes: ['measure'] });
要点总结
- Node.js 主线程单线程,事件循环处理并发
- libuv 线程池处理 fs/crypto/zlib(默认 4 线程)
- 网络 I/O 使用系统原生异步机制,不占用线程池
- worker_threads 提供真正的多线程能力
- cluster 多进程利用多核 CPU
- CPU 密集任务使用 Worker 或分片执行
📝 发现内容有误?点击此处直接编辑