通常,node.js 中的 I/O 是非阻塞的。您可以通过同时向服务器发出多个请求来测试这一点。例如,如果每个请求需要 1 秒来处理,则阻塞服务器将花费 2 秒来处理 2 个并发请求,但非阻塞服务器将只需要 1 秒多一点的时间来处理这两个请求。
但是,您可以通过使用故意阻止请求同步请求 https://github.com/ForbesLindesay/sync-request模块而不是request https://github.com/request/request。显然,不建议服务器这样做。
下面是一些代码来演示阻塞 I/O 和非阻塞 I/O 之间的区别:
var req = require('request');
var sync = require('sync-request');
// Load example.com N times (yes, it's a real website):
var N = 10;
console.log('BLOCKING test ==========');
var start = new Date().valueOf();
for (var i=0;i<N;i++) {
var res = sync('GET','http://www.example.com')
console.log('Downloaded ' + res.getBody().length + ' bytes');
}
var end = new Date().valueOf();
console.log('Total time: ' + (end-start) + 'ms');
console.log('NON-BLOCKING test ======');
var loaded = 0;
var start = new Date().valueOf();
for (var i=0;i<N;i++) {
req('http://www.example.com',function( err, response, body ) {
loaded++;
console.log('Downloaded ' + body.length + ' bytes');
if (loaded == N) {
var end = new Date().valueOf();
console.log('Total time: ' + (end-start) + 'ms');
}
})
}
运行上面的代码,您将看到非阻塞测试处理所有请求所需的时间与处理单个请求的时间大致相同(例如,如果设置 N = 10,则非阻塞代码执行 10 次比阻塞代码更快)。这清楚地表明请求是非阻塞的。
补充答案:
您还提到您担心您的进程会占用 CPU 资源。但在您的代码中,您没有对 CPU 效用进行基准测试。您混合了网络请求时间(I/O,我们知道它是非阻塞的)和 CPU 处理时间。要测量请求处于阻塞模式的时间,请将代码更改为:
async.waterfall([
function(cb) {
request(someUrl1, function(err, res, body) {
console.time('1');
// load and parse the given web page.
console.timeEnd('1');
// make a callback with data parsed from the web page
});
},
function(someParameters, cb) {
request({url: someUrl2, method: 'POST', form: {/* data */}}, function(err, res, body) {
console.time('2');
// more computation
console.timeEnd('2');
// make a callback with a session cookie given by the visited url
});
},
function(jar, cb) {
request({url: someUrl3, method: 'GET', jar: jar /* cookie from the previous callback */}, function(err, res, body) {
console.time('3');
// do more parsing + computation
console.timeEnd('3');
// make another callback with the results
});
},
function(moreParameters, cb) {
request({url: someUrl4, method: 'POST', jar: jar, form : {/*data*/}}, function(err, res, body) {
console.time('4');
// some more computation.
console.timeEnd('4');
// make final callback
});
}
], function (err, result) {
res.status(200).send();
});
您的代码仅阻塞在“更多计算”部分。因此,您可以完全忽略等待其他部分执行所花费的任何时间。事实上,这正是节点可以同时服务多个请求的方式。在等待其他部分调用各自的回调时(您提到这可能需要长达 1 秒),节点可以执行其他 javascript 代码并处理其他请求。