Node.js 并发处理机制详解
🤔 问题:单个接口需要3秒返回,后面的请求会阻塞吗?
答案:不会阻塞! Node.js 使用事件循环机制,支持非阻塞并发处理。
🔍 核心概念
1. 事件循环 (Event Loop)
Node.js 是单线程的,但通过事件循环实现并发:
javascript
// 伪代码展示事件循环工作原理
while (eventLoop.hasEvents()) {
const event = eventLoop.getNextEvent();
if (event.isAsync()) {
// 异步操作交给系统处理,不阻塞主线程
system.handleAsync(event, callback);
} else {
// 同步操作立即执行
event.execute();
}
}2. 非阻塞 I/O
当遇到耗时操作时:
- 阻塞式:线程等待操作完成
- 非阻塞式:操作交给系统,线程继续处理其他请求
🧪 实际演示
场景模拟
javascript
// 慢接口 - 需要3秒处理时间
app.get('/slow', async (req, res) => {
console.log('慢接口开始处理');
// 这里的 setTimeout 是异步的,不会阻塞其他请求
await new Promise(resolve => setTimeout(resolve, 3000));
console.log('慢接口处理完成');
res.json({ message: '慢接口响应' });
});
// 快接口 - 立即响应
app.get('/fast', (req, res) => {
console.log('快接口立即响应');
res.json({ message: '快接口响应' });
});并发测试结果
当同时发起多个请求时:
时间轴演示:
0ms: 请求1(/slow) 开始处理
10ms: 请求2(/fast) 立即响应 ✅
20ms: 请求3(/slow) 开始处理
30ms: 请求4(/fast) 立即响应 ✅
3000ms: 请求1(/slow) 完成响应 ✅
3020ms: 请求3(/slow) 完成响应 ✅🔧 关键技术点
1. 异步操作类型
javascript
// 这些操作都是非阻塞的:
- setTimeout/setInterval
- 文件读写 (fs.readFile)
- 数据库查询
- HTTP 请求
- Promise/async-await2. 事件循环阶段
┌───────────────────────────┐
┌─>│ timers │ ← setTimeout, setInterval
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │ ← I/O 回调
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │ ← 内部使用
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ poll │ ← 获取新的 I/O 事件
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ check │ ← setImmediate
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │ ← 关闭回调
└───────────────────────────┘3. 线程池
某些操作使用线程池处理:
- 文件系统操作
- DNS 查询
- CPU 密集型任务
📊 性能对比
传统多线程模型
请求1 → 线程1 (阻塞3秒)
请求2 → 线程2 (阻塞3秒)
请求3 → 线程3 (阻塞3秒)
...
资源消耗:每个线程 ~2MB 内存Node.js 事件循环模型
请求1 → 事件循环 → 异步处理 (非阻塞)
请求2 → 事件循环 → 立即响应
请求3 → 事件循环 → 异步处理 (非阻塞)
...
资源消耗:单线程 + 事件循环⚠️ 注意事项
1. CPU 密集型任务会阻塞
javascript
// ❌ 这会阻塞事件循环
app.get('/cpu-intensive', (req, res) => {
let result = 0;
for (let i = 0; i < 10000000000; i++) {
result += i; // 同步计算,会阻塞
}
res.json({ result });
});
// ✅ 正确做法:使用 Worker Threads 或分批处理
const { Worker } = require('worker_threads');
app.get('/cpu-intensive-async', (req, res) => {
const worker = new Worker('./cpu-worker.js');
worker.postMessage({ task: 'heavy-calculation' });
worker.on('message', (result) => {
res.json({ result });
});
});2. 回调地狱和错误处理
javascript
// ❌ 回调地狱
app.get('/callback-hell', (req, res) => {
db.query('SELECT * FROM users', (err1, users) => {
if (err1) throw err1;
db.query('SELECT * FROM posts', (err2, posts) => {
if (err2) throw err2;
// 更多嵌套...
});
});
});
// ✅ 使用 async/await
app.get('/clean-async', async (req, res) => {
try {
const users = await db.query('SELECT * FROM users');
const posts = await db.query('SELECT * FROM posts');
res.json({ users, posts });
} catch (error) {
res.status(500).json({ error: error.message });
}
});🎯 总结
- Node.js 不会因为单个慢接口而阻塞其他请求
- 事件循环机制实现高并发处理
- 异步 I/O 操作是关键
- 避免 CPU 密集型同步操作
- 合理使用 async/await 处理异步流程
🚀 最佳实践
javascript
// 1. 使用异步操作
const data = await database.query('SELECT * FROM table');
// 2. 设置合理的超时
const timeout = setTimeout(() => {
res.status(408).json({ error: 'Request timeout' });
}, 30000);
// 3. 错误处理
try {
const result = await someAsyncOperation();
clearTimeout(timeout);
res.json(result);
} catch (error) {
clearTimeout(timeout);
res.status(500).json({ error: error.message });
}
// 4. 使用连接池
const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
// ...
});这就是为什么 Node.js 特别适合 I/O 密集型应用,如 Web API、实时应用等场景!