例如,有一个请求进来,需要200毫秒计算,由于Node.js使用单线程等原因,这将会阻塞其他请求。
这意味着每秒处理的请求数将因为实际响应计算时间的原因而显著下降。
但是,这听起来不太对,所以我想知道真正发生了什么,因为我无法想象事情是如何运作的。
Node.js内部所有内容都是并行运行的。然而,你自己的代码严格按照顺序运行。如果在node.js中休眠一秒钟,服务器也会休眠一秒钟。它不适用于需要大量计算的请求。I/O是并行的,你的代码通过回调函数进行I/O(所以在等待I/O时,你的代码不会运行)。
在大多数现代平台上,node.js确实使用线程进行I/O。它使用libev,在平台上最适合的地方使用线程。
你说得完全正确。Node.js开发人员必须意识到这一点,否则他们的应用程序将完全无法执行,如果长时间运行的代码不是异步的话。
所有需要花费“很长时间”的操作都必须以异步方式完成。
这基本上是正确的,至少如果您不使用新的cluster
功能来平衡多个自动生成的工作进程之间的传入连接。然而,如果您使用它,大多数其他请求仍将快速完成。
编辑:工作进程是进程。
你可以将事件循环想象成排队支付账单的10个人。如果有人花费太多时间来支付账单(因此阻塞了事件循环),其他人只能在那里等待他们的机会..一直等待...
换句话说:
由于事件循环在单个线程上运行,因此非常重要的是不要通过在回调函数或同步I/O中进行繁重的计算来阻塞其执行。在回调函数中遍历大量值/对象或执行耗时的计算会防止事件循环进一步处理队列中的其他事件。
以下是一些代码,可以实际看到阻塞/非阻塞的示例:
通过这个例子(CPU计算时间长,无I/O操作):
var net = require('net');
handler = function(req, res) {
console.log('hello');
for (i = 0; i < 10000000000; i++) { a = i + 5; }
}
net.createServer(handler).listen(80);
如果在浏览器中进行两个请求,服务器控制台只会显示一个“hello”,这意味着第二个请求不能被处理,因为第一个请求阻塞了Node.js线程。
如果我们做一个I/O任务(将2GB的数据写入磁盘,在我的测试中需要几秒钟,甚至在SSD上):
http = require('http');
fs = require('fs');
buffer = Buffer.alloc(2*1000*1000*1000);
first = true;
done = false;
write = function() {
fs.writeFile('big.bin', buffer, function() { done = true; });
}
handler = function(req, res) {
if (first) {
first = false;
res.end('Starting write..')
write();
return;
}
if (done) {
res.end("write done.");
} else {
res.end('writing ongoing.');
}
}
http.createServer(handler).listen(80);
可以看到,几秒钟的I/O写入任务 write
是非阻塞的:如果在此期间进行其他请求,您将看到“writing ongoing.”!这证实了Node.js的众所周知的非阻塞IO特性。