Nodejs非阻塞的理解

someone
  • 490

主程序:

var http = require('http');
var url = require('url');
var cp = require('child_process');

function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    if( pathname == '/wait' ) {
        console.log("Will wait!");
        cp.exec('node applications\\demo\\block.js', [], myCallback);
        console.log(new Date());
    }
    else{
        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Hello!\n');
        response.end();
    }

    console.log('Cool ' + (new Date()));

    function myCallback(){
        console.log("ok: " + (new Date()));
        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Thanks for waiting!\n');
        response.end();
    }
}
http.createServer(onRequest).listen(8081);
console.log('Server started');

exec调用的block.js是(即休眠10秒):

var startTime = new Date().getTime();
while (new Date().getTime() < startTime + 10000);

如果我访问http://localhost:8081/wait,会进入pathname == '/wait'的语句,并且通过exec调用block.js休眠10秒。这时我在休眠结束之前迅速的访问http://localhost:8081/,迅速返回结果。这个我认为是正常的。

但是如果我同时打开三个http://localhost:8081/wait链接,发现第一个链接大约花10秒,第二个链接大约花20秒,第三个链接大约花30秒(从console的输出看他们是串行的)。通过任务管理器看到只多了一个node进程(win 7操作系统,四核)。难道这三次访问exec不应该开三个block.js进程吗?请教该如何理解这段代码和这个现象?
Nodejs所说的能同时处理多个请求到底该如何理解呢?

附上console输出:

Server started
Will wait!
Sun Jan 25 2015 00:59:50 GMT+0800 (中国标准时间)
Cool Sun Jan 25 2015 00:59:50 GMT+0800 (中国标准时间)
ok: Sun Jan 25 2015 01:00:01 GMT+0800 (中国标准时间)
Will wait!
Sun Jan 25 2015 01:00:21 GMT+0800 (中国标准时间)
Cool Sun Jan 25 2015 01:00:21 GMT+0800 (中国标准时间)
ok: Sun Jan 25 2015 01:00:31 GMT+0800 (中国标准时间)
Will wait!
Sun Jan 25 2015 01:00:31 GMT+0800 (中国标准时间)
Cool Sun Jan 25 2015 01:00:31 GMT+0800 (中国标准时间)
ok: Sun Jan 25 2015 01:00:41 GMT+0800 (中国标准时间)
回复
阅读 4.5k
1 个回答

开小窗口用curl测试,行为和预期一致

图片描述

所以可以判断这是浏览器的行为


另外,一般意义上说node非阻塞不是题主的busy-wait+子进程的实现方式(动辄cpu100%完全顶不住压力),而是事件队列,单进程单线程的非阻塞,拿题主的例子改改是这样的

jsvar http = require('http');
var url = require('url');
var cp = require('child_process');

function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    if( pathname == '/wait' ) {
        console.log("Will wait!");
        setTimeout(myCallback, 10000);//等待10秒
        console.log(new Date());
    }
    else{
        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Hello!\n');
        response.end();
    }

    console.log('Cool ' + (new Date()));

    function myCallback(){
        console.log("ok: " + (new Date()));
        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Thanks for waiting!\n');
        response.end();
    }
}
http.createServer(onRequest).listen(8081);
console.log('Server started');

简单来说,就是事件(比如setTimeout,或者更常见的网络/文件IO等)来了以后,如果当前状态是空闲的,那么会立刻处理;而如果正在执行其他代码,则会推入队列中,等待执行完毕后才处理。所以微观来说,一个node进程同一时间永远最多只在处理一个请求。但由于多数业务都是IO密集型,主要瓶颈往往落在磁盘IO,数据库或其他服务的网络IO等等,实际的业务CPU运算很少,此时node多数时间都处于等待IO回调的空闲状态,因此宏观上来看一个node进程往往可以同时处理大量的业务请求

宣传栏