2

我正在从Node Beginner Book和随后的电子书购买中学习 node.js。在书中,Manuel Kiessling 解释了这样一行阻塞代码:

fs.readFileSync(blah);

将阻止整个节点进程和所有请求进入。这对于多用户网站来说真的很糟糕!

这是示例,Kiessling 使用:

exec("ls -lah", function( error, stdout, stderr ) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write(stdout);
    response.end();
});

这是欺骗我的代码。他说,ls -lah可以很容易地用更耗时的操作(如find / -name "*"数据库查找)代替。我假设由于异步回调,昂贵的阻塞操作会以某种方式在后台显式运行。

所以我必须用这段代码来测试我的理论:

var http = require("http");
var url = require("url");

badSleep = function(ms) {
    var now = new Date();
    var finishAtDate = now.getTime() + ms;
    console.log("CPU burning sleep for " + ms + " milliseconds");
    while(new Date() < finishAtDate) {
       // do nothing
    }
};

asyncWrapper = function(callback) {
    //badSleep(3000);
    callback();
}

http.createServer(function(request, response) {
    var pathname = url.parse(request.url).pathname;
    console.log("Serve up " + pathname);
    if (pathname == '/favicon.ico') {
        response.writeHead(404);
        response.end();
    } else {
        asyncWrapper(function() {
            badSleep(3000);
            response.writeHead(200, {"Content-Type": "text/plain"});
            response.write("\nI was wrong " + new Date());
            response.end();
        });
    }
}).listen(8888);

问题是,无论我把睡眠放在哪里,它仍然会阻塞节点事件循环。回调并不能解决阻塞问题。SO的好用户也在评论中告诉我这一点。

那怎么exec做???我很困惑,所以我去查看了github 上的子进程代码。我发现exec打电话了spawn!!!它使一个子进程!谜团已揭开。异步代码并不能“解决”阻塞问题,但 spawn 可以。

这引出了我的问题。express 是否以某种方式解决了阻塞问题,还是您仍然需要担心它?

PS:这个问题是一个重大的重写。我想请求以下 SO 用户的原谅,并感谢他们对我的耐心。我肯定在这里学到了一些东西。

4

3 回答 3

4

有人发表了关于理解节点js事件循环的评论。对,就是这样。如果将其包装在异步调用中,则可以发出阻塞调用,因为您没有阻塞节点事件循环。

如果你用异步调用包装同步调用,你仍然会遇到阻塞。例如,如果你写这样的东西:

fs.readFile("file1.txt", function(err, data1) {
   var data2 = fs.readFileSync("file2.txt");
});

该进程在读取 file1.txt 时不会被阻塞,因为您使用的是异步调用,但是,一旦完成读取 file1 并到达读取 file2 的行,它就会阻塞

通过在异步/非阻塞调用中发出同步/阻塞调用,您只是在延迟阻塞。

你是对的,阻止整个网站真的很糟糕,这就是为什么你不应该经常发出阻止呼叫的原因。由于 node.js 是从头开始编写的,因此大多数 I/O 调用默认情况下都是异步的,您应该尽可能多地使用它们而不是同步调用。

问题是,快递会自动为您处理这个问题,还是您仍然需要担心?

你仍然需要担心它。

于 2013-04-08T13:11:02.327 回答
2

问题是,快递会自动为您处理这个问题,还是您仍然需要担心?

你仍然需要担心它。NodeJS 是单线程的,这意味着每个同步操作都会完全阻塞它,无论它在哪里调用。Express 和任何其他框架都不能在不阻塞服务器的情况下使用同步操作。简单的

var x = 1;

已经阻塞了整个服务器,直到它完成创建新变量并为其分配新值。

异步架构的全部意义在于它比线程更有效。不要被愚弄,异步编程比线程更难,因为没有隔离。如果一个线程失败,其他线程仍然可以工作,而在异步服务器中,一个异常可能会破坏整个服务器。

问题是您可能会阻塞主节点事件循环!

这句话表明 NodeJS 除了主事件循环之外还有更多的东西。这不是真的。每个代码都在主循环中调用。

也看看这个:

事件循环与多线程阻塞 IO

于 2013-04-08T13:19:30.323 回答
1

任何对非异步函数的调用都会“阻塞”,即使它被包装在另一个函数中。唯一的例外是包装函数是否可以将处理推迟到另一个线程/进程(例如,像clusterAPI)。

于 2013-04-08T11:19:48.600 回答