文档
Node.js 进阶篇:异步编程与性能优化
背景
Node.js 的异步模型是其性能核心。理解 Promise、async/await、Worker Threads 以及 Stream,是编写高性能 Node.js 应用的关键。
核心概念
1. Promise 链与 async/await
// 回调地狱 ❌
fs.readFile('a.txt', (err, dataA) => {
fs.readFile('b.txt', (err, dataB) => {
fs.readFile('c.txt', (err, dataC) => {
console.log(dataA + dataB + dataC);
});
});
});
// Promise 链 ✅
Promise.all([
fs.promises.readFile('a.txt'),
fs.promises.readFile('b.txt'),
fs.promises.readFile('c.txt'),
]).then(buffers => console.log(buffers.join('')));
// async/await 最优雅 ✅
const [a, b, c] = await Promise.all([
fs.promises.readFile('a.txt'),
fs.promises.readFile('b.txt'),
fs.promises.readFile('c.txt'),
]);
2. Stream — 流式处理大数据
const fs = require('fs');
const zlib = require('zlib');
// 边读边压缩边写 — 内存占用恒定
fs.createReadStream('large.log')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('large.log.gz'));
3. Worker Threads — 突破单线程限制
// main.js
const { Worker } = require('worker_threads');
function runCPUIntensiveTask(data) {
return new Promise((resolve, reject) => {
const worker = new Worker('./worker.js', { workerData: data });
worker.on('message', resolve);
worker.on('error', reject);
});
}
// worker.js
const { parentPort, workerData } = require('worker_threads');
// 执行 CPU 密集运算
let result = fibonacci(workerData.n);
parentPort.postMessage(result);
4. Cluster — 利用多核 CPU
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
os.cpus().forEach(() => cluster.fork());
cluster.on('exit', (worker) => cluster.fork()); // 自动重启
} else {
require('./app'); // 启动 Express 服务
}
性能优化检查清单
| 优化项 | 方法 |
|---|---|
| 数据库查询 | 使用连接池、添加索引、避免 N+1 查询 |
| 缓存 | Redis 缓存热点数据、设置合理 TTL |
| 压缩 | 启用 gzip/br 压缩响应 |
| 静态资源 | 使用 CDN 或 Nginx 反代 |
| 内存泄漏 | 使用 --inspect + Chrome DevTools 排查 |
| 事件循环阻塞 | 避免同步 I/O、大循环拆分为 setImmediate |
分步操作:压测与排查
# 1. 使用 autocannon 压测
npm i -g autocannon
autocannon -c 100 -d 10 http://localhost:3000/users
# 2. 使用 clinic.js 诊断性能
npm i -g clinic
clinic doctor -- node app.js
# 然后运行 autocannon,分析生成的 HTML 报告
# 3. 查找耗时中间件
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
console.log(`${req.method} ${req.url} ${Date.now() - start}ms`);
});
next();
});
思考题
Promise.all和Promise.allSettled有什么区别?什么场景用哪个?- 为什么 Worker Threads 不适合处理大量 I/O 操作?
- Stream 的背压(backpressure)机制是如何工作的?